home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / bltHtext.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-26  |  102.4 KB  |  3,564 lines

  1. /*
  2.  * bltHtext.c --
  3.  *
  4.  *    This module implements a hypertext widget for the
  5.  *    Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortious action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  * Hypertext widget created by George Howlett.
  26.  */
  27.  
  28. /*
  29.  * To do:
  30.  *
  31.  * 1) Fix scroll unit round off errors.
  32.  *
  33.  * 2) Bug in reporting errors in Tcl evaluations.
  34.  *
  35.  * 3) Selections of text. (characters, word, line)
  36.  *
  37.  * 4) Tabstops for easier placement of text and child widgets.
  38.  *    Use variable "tabstops" to set/reset tabstops.
  39.  *
  40.  * 5) Better error checking.
  41.  *
  42.  */
  43.  
  44. #include "blt.h"
  45. #include <X11/Xutil.h>
  46.  
  47. #if (TK_MINOR_VERSION > 0)
  48. #define Cursor Tk_Cursor
  49. #endif
  50.  
  51. #ifndef HTEXT_VERSION
  52. #define HTEXT_VERSION "2.4"
  53. #endif
  54.  
  55. #define HTEXT_CMDNAME     "blt_htext"
  56.  
  57. #define LINES_ALLOC_CHUNK 512
  58.  
  59. #define BLT_MIN(a,b)    (((a)<(b))?(a):(b))
  60. #define BLT_MAX(a,b)    (((a)>(b))?(a):(b))
  61.  
  62. /*
  63.  * Flags passed to TkMeasureChars: taken from tkInt.h
  64.  */
  65. #define TK_WHOLE_WORDS          1
  66. #define TK_AT_LEAST_ONE         2
  67. #define TK_PARTIAL_OK           4
  68.  
  69. /*
  70.  * The following enumerated values are used as bit flags.
  71.  */
  72. typedef enum {
  73.     FILL_NONE, FILL_X, FILL_Y, FILL_BOTH
  74. } FillFlags;
  75.  
  76. static int ParseFill _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  77.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  78. static char *PrintFill _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  79.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  80.  
  81. static Tk_CustomOption FillOption =
  82. {
  83.     ParseFill, PrintFill, (ClientData)0
  84. };
  85.  
  86. /*
  87.  * Justify option values
  88.  */
  89. typedef enum {
  90.     JUSTIFY_CENTER, JUSTIFY_TOP, JUSTIFY_BOTTOM
  91. } Justify;
  92.  
  93. static int ParseJustify _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
  94. static char *PrintJustify _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  95.  
  96. static Tk_CustomOption JustifyOption =
  97. {
  98.     ParseJustify, PrintJustify, (ClientData)0
  99. };
  100.  
  101. static char *fillStrings[] =
  102. {
  103.     "none", "x", "y", "both"
  104. };
  105. static char *justifyStrings[] =
  106. {
  107.     "center", "top", "bottom"
  108. };
  109.  
  110. typedef struct Child {
  111.     struct Htext *textPtr;    /* Pointer to parent's Htext structure */
  112.     Tk_Window tkwin;        /* Widget window */
  113.     int flags;
  114.  
  115.     int x, y;            /* Origin of child subwindow in text */
  116.  
  117.     /* Dimensions of the cavity surrounding the child window */
  118.  
  119.     unsigned int cavityWidth, cavityHeight;
  120.  
  121.     /*
  122.      * Dimensions of the child window.  Saved for later comparisons to
  123.      * check for resizing.
  124.      */
  125.     unsigned int windowWidth, windowHeight;
  126.  
  127.     int precedingTextEnd;    /* Number of characters of text */
  128.     int precedingTextWidth;    /* Width of normal text preceding child */
  129.  
  130.     Tk_Anchor anchor;
  131.     Justify justify;        /* Justification of region wrt to line */
  132.  
  133.     int reqWidth, reqHeight;    /* Requested dimension of cavity */
  134.     double relWidth, relHeight;    /* Relative dimensions of cavity.
  135.                  * Sizes are calculated with respect
  136.                  * the size of the viewport */
  137.     int padX, padY;        /* Extra padding to frame around */
  138.     int ipadX, ipadY;        /* Internal padding for window */
  139.  
  140.     FillFlags fill;        /* Fill style flag */
  141.  
  142. } Child;
  143.  
  144. /*
  145.  * Flag bits children subwindows:
  146.  */
  147. #define VISIBLE        4    /* Subwindow is visible in the viewport. */
  148.  
  149. /*
  150.  * Information used for parsing configuration specs:
  151.  */
  152.  
  153. /*
  154.  * Defaults for children:
  155.  */
  156.  
  157. #define DEF_CHILD_ANCHOR        "center"
  158. #define DEF_CHILD_FILL        "none"
  159. #define DEF_CHILD_HEIGHT    "0"
  160. #define DEF_CHILD_JUSTIFY    "center"
  161. #define DEF_CHILD_PAD_X        "0"
  162. #define DEF_CHILD_PAD_Y        "0"
  163. #define DEF_CHILD_REL_HEIGHT    "0.0"
  164. #define DEF_CHILD_REL_WIDTH      "0.0"
  165. #define DEF_CHILD_WIDTH      "0"
  166.  
  167. static Tk_ConfigSpec childConfigSpecs[] =
  168. {
  169.     {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL,
  170.     DEF_CHILD_ANCHOR, Tk_Offset(Child, anchor), 
  171.         TK_CONFIG_DONT_SET_DEFAULT},
  172.     {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL,
  173.     DEF_CHILD_FILL, Tk_Offset(Child, fill),
  174.     TK_CONFIG_DONT_SET_DEFAULT, &FillOption},
  175.     {TK_CONFIG_PIXELS, "-height", (char *)NULL, (char *)NULL,
  176.     DEF_CHILD_HEIGHT, Tk_Offset(Child, reqHeight), 
  177.         TK_CONFIG_DONT_SET_DEFAULT},
  178.     {TK_CONFIG_CUSTOM, "-justify", (char *)NULL, (char *)NULL,
  179.     DEF_CHILD_JUSTIFY, Tk_Offset(Child, justify),
  180.     TK_CONFIG_DONT_SET_DEFAULT, &JustifyOption},
  181.     {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL,
  182.     DEF_CHILD_PAD_X, Tk_Offset(Child, padX), 
  183.         TK_CONFIG_DONT_SET_DEFAULT},
  184.     {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL,
  185.     DEF_CHILD_PAD_Y, Tk_Offset(Child, padY),
  186.         TK_CONFIG_DONT_SET_DEFAULT},
  187.     {TK_CONFIG_DOUBLE, "-relheight", (char *)NULL, (char *)NULL,
  188.     DEF_CHILD_REL_HEIGHT, Tk_Offset(Child, relHeight),
  189.         TK_CONFIG_DONT_SET_DEFAULT},
  190.     {TK_CONFIG_DOUBLE, "-relwidth", (char *)NULL, (char *)NULL,
  191.     DEF_CHILD_REL_WIDTH, Tk_Offset(Child, relWidth),
  192.         TK_CONFIG_DONT_SET_DEFAULT},
  193.     {TK_CONFIG_PIXELS, "-width", (char *)NULL, (char *)NULL,
  194.     DEF_CHILD_WIDTH, Tk_Offset(Child, reqWidth),
  195.         TK_CONFIG_DONT_SET_DEFAULT},
  196.     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
  197.     (char *)NULL, 0, 0}
  198. };
  199.  
  200. /*
  201.  * Structure to contain the contents of a single line of text and the
  202.  * children on that line.
  203.  *
  204.  * Individual lines are not configurable, although changes to the size
  205.  * of children do effect its values.
  206. */
  207.  
  208. typedef struct {
  209.     int offset;            /* Pixel offset from world coordinate 0,0 */
  210.  
  211.     short int height;        /* Height of line */
  212.     short int width;        /* Width of line */
  213.     short int baseline;        /* Baseline of text */
  214.     short int numChars;        /* Number of characters in normal text */
  215.     char *text;            /* The plain text on the line */
  216.  
  217.     Blt_LinkedList windowList;    /* List of subwindows on the line of text */
  218.  
  219. } Line;
  220.  
  221.  
  222. typedef struct {
  223.     Tk_3DBorder border;        /* Border and background for selected
  224.                      * characters. */
  225.     int borderWidth;        /* Width of border around selection. */
  226.     XColor *activeFg;        /* Foreground color for selected text. */
  227.     GC gc;            /* For drawing selected text. */
  228.  
  229.     int first;            /* Position of first selected character
  230.                  * (-1 means nothing is selected). */
  231.     int last;            /* Position of last selected character.
  232.                  * (-1  means nothing is selected). */
  233.     int anchor;            /* Fixed end of selection
  234.                      * (i.e. "select to" operation will
  235.                      * use this as one end of the selection).*/
  236. } Selection;
  237.  
  238. typedef struct {
  239.     long int x, y;
  240.     long int width, height;
  241. } ViewPort;
  242.  
  243. /*
  244.  * Hypertext widget.
  245.  */
  246. typedef struct Htext {
  247.     Tk_Window tkwin;        /* Window that embodies the widget.
  248.                                  * NULL means that the window has been
  249.                                  * destroyed but the data structures
  250.                                  * haven't yet been cleaned up.*/
  251.     Display *display;        /* Display containing widget; needed,
  252.                                  * among other things, to release
  253.                                  * resources after tkwin has already
  254.                                  * gone away. */
  255.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  256.     unsigned int flags;
  257.  
  258.     /* User-configurable fields */
  259.  
  260.     Tk_3DBorder border;        /* Don't current use borders (only color) */
  261.     XColor *normalFg;
  262.     int reqLineNum;        /* Line requested by "gotoline" command */
  263.     int reqWidth, reqHeight;    /* Requested dimensions of the text window */
  264.     int maxWidth, maxHeight;
  265.     Cursor cursor;        /* X Cursor for child */
  266.  
  267.     char *yScrollCmd;        /* Name of vertical scrollbar to invoke */
  268.     int scrollY;        /* # of pixels per vert scroll */
  269.     char *xScrollCmd;        /* Name of horizontal scroll bar to invoke */
  270.     int scrollX;        /* # of pixels per horiz scroll */
  271.     int lineSpacing;        /* # of pixels between lines */
  272.     int specChar;        /* Special character designating a TCL
  273.                      * command block in a hypertext file. */
  274.     XFontStruct *fontPtr;    /* Font for normal text */
  275.     char *fileName;        /* Name of hypertext file  */
  276.     char *text;            /* Text */
  277.  
  278.  
  279.     /*
  280.      * The view port is the width and height of the window and the
  281.      * origin of the viewport (upper left corner) in world coordinates.
  282.      */
  283.     ViewPort vPort;        /* Position and size of viewport in
  284.                  * virtual coordinates */
  285.     int pendingX, pendingY;    /* New upper-left corner (origin) of
  286.                  * the viewport (not yet posted) */
  287.  
  288.     int first, last;        /* Range of lines displayed */
  289.  
  290.     Tcl_HashTable subwindows;    /* Table of child sub-windows */
  291.     GC gc;            /* Graphics context for normal text */
  292.  
  293. #ifdef notdef
  294.     Selection selection;
  295. #endif
  296.     /*
  297.      * Scanning information:
  298.      */
  299.     XPoint scanMark;        /* Anchor position of scan */
  300.     XPoint scanPt;        /* x,y position where the scan started. */
  301.  
  302.     unsigned int width, height;    /* Size of the window: saved to recognize
  303.                  * when the viewport is resized. */
  304.     char *intBuffer;        /* Internal text buffer */
  305.     Line **linePtrPtr;        /* Array of text lines */
  306.     unsigned int numLines;    /* # of line entered into array. */
  307.     unsigned int arraySize;    /* Size of array allocated. */
  308.  
  309.     int relief;            /* Theoretically, the relief.  Does nothing
  310.                  * but satisfy SpecTcl currently */
  311. } Htext;
  312.  
  313. /*
  314.  * Bit flags for the hypertext widget:
  315.  */
  316. #define REDRAW_PENDING    1    /* A DoWhenIdle handler has already
  317.                  * been queued to redraw the window */
  318. #define IGNORE_EXPOSURES (1<<1)    /* Ignore exposure events in the text
  319.                  * window.  Potentially many expose
  320.                  * events can occur while rearranging
  321.                  * subwindows during a single call to
  322.                  * the DisplayText.  */
  323. #define VIEW_RESIZED    (1<<2)    /* Size of viewport (window) has
  324.                    changed */
  325. #define VIEW_MOVED     (1<<3)    /* Position of viewport has moved.
  326.                  * This occurs when scrolling or
  327.                  * goto-ing a new line */
  328. #define REQUEST_LAYOUT     (1<<4)    /* Something has happened which
  329.                  * requires the layout of text and
  330.                  * child window positions to be
  331.                  * recalculated.  The following
  332.                  * actions may cause this:
  333.                  *
  334.                  * 1) the contents of the hypertext
  335.                  *    has changed by either the -file or
  336.                  *    -text options.
  337.                  *
  338.                  * 2) a text attribute has changed
  339.                  *    (line spacing, font, etc)
  340.                  *
  341.                  * 3) a subwindow has been resized or
  342.                  *    moved.
  343.                  *
  344.                  * 4) a child configuration option has
  345.                  *    changed.
  346.                  */
  347. #define LAYOUT_CHANGED     (1<<5)    /* The layout was recalculated and the
  348.                  * size of the world (text layout) has
  349.                  * changed. */
  350. #define REQUEST_GOTO     (1<<6)    /* Indicates the starting text line
  351.                  * number has changed. To be reflected
  352.                  * the next time the widget is
  353.                  * redrawn. */
  354.  
  355. #define DEF_HTEXT_BG_COLOR    BG2
  356. #define DEF_HTEXT_BG_MONO    WHITE
  357. #define DEF_HTEXT_CURSOR    "pencil"
  358. #define DEF_HTEXT_FG_COLOR    BLACK
  359. #define DEF_HTEXT_FG_MONO    BLACK
  360. #define DEF_HTEXT_FILE_NAME    (char *)NULL
  361. #define DEF_HTEXT_FONT        "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
  362. #define DEF_HTEXT_HEIGHT    "0"
  363. #define DEF_HTEXT_LINE_SPACING    "1"
  364. #define DEF_HTEXT_MAX_HEIGHT    "9.5i"
  365. #define DEF_HTEXT_MAX_WIDTH     "6.5i"
  366. #define DEF_HTEXT_RELIEF    "flat"
  367. #define DEF_HTEXT_SCROLL_UNITS    "10"
  368. #define DEF_HTEXT_SPEC_CHAR    "0x25"
  369. #define DEF_HTEXT_TEXT        (char *)NULL
  370. #define DEF_HTEXT_WIDTH        "0"
  371.  
  372. static Tk_ConfigSpec configSpecs[] =
  373. {
  374.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  375.     DEF_HTEXT_BG_COLOR, Tk_Offset(Htext, border),
  376.     TK_CONFIG_COLOR_ONLY},
  377.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  378.     DEF_HTEXT_BG_MONO, Tk_Offset(Htext, border), TK_CONFIG_MONO_ONLY},
  379.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
  380.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  381.     DEF_HTEXT_CURSOR, Tk_Offset(Htext, cursor), TK_CONFIG_NULL_OK},
  382.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
  383.     {TK_CONFIG_STRING, "-filename", "fileName", "FileName",
  384.     DEF_HTEXT_FILE_NAME, Tk_Offset(Htext, fileName), TK_CONFIG_NULL_OK},
  385.     {TK_CONFIG_FONT, "-font", "font", "Font",
  386.     DEF_HTEXT_FONT, Tk_Offset(Htext, fontPtr), 0},
  387.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  388.     DEF_HTEXT_FG_COLOR, Tk_Offset(Htext, normalFg), TK_CONFIG_COLOR_ONLY},
  389.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  390.     DEF_HTEXT_FG_MONO, Tk_Offset(Htext, normalFg), TK_CONFIG_MONO_ONLY},
  391.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  392.     DEF_HTEXT_HEIGHT, Tk_Offset(Htext, reqHeight), 
  393.         TK_CONFIG_DONT_SET_DEFAULT},
  394.     {TK_CONFIG_PIXELS, "-linespacing", "lineSpacing", "LineSpacing",
  395.     DEF_HTEXT_LINE_SPACING, Tk_Offset(Htext, lineSpacing), 
  396.         TK_CONFIG_DONT_SET_DEFAULT},
  397.     {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight",
  398.     DEF_HTEXT_MAX_HEIGHT, Tk_Offset(Htext, maxHeight), 0},
  399.     {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth",
  400.     DEF_HTEXT_MAX_WIDTH, Tk_Offset(Htext, maxWidth), 0},
  401.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  402.     DEF_HTEXT_RELIEF, Tk_Offset(Htext, relief), 0},
  403.     {TK_CONFIG_INT, "-specialchar", "specialChar", "SpecialChar",
  404.     DEF_HTEXT_SPEC_CHAR, Tk_Offset(Htext, specChar), 0},
  405.     {TK_CONFIG_STRING, "-text", "text", "Text",
  406.     DEF_HTEXT_TEXT, Tk_Offset(Htext, text), TK_CONFIG_NULL_OK},
  407.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  408.     DEF_HTEXT_WIDTH, Tk_Offset(Htext, reqWidth), 
  409.         TK_CONFIG_DONT_SET_DEFAULT},
  410.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  411.     (char *)NULL, Tk_Offset(Htext, xScrollCmd), TK_CONFIG_NULL_OK},
  412.     {TK_CONFIG_PIXELS, "-xscrollunits", "xScrollUnits", "ScrollUnits",
  413.     DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollX), 
  414.         TK_CONFIG_DONT_SET_DEFAULT},
  415.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  416.     (char *)NULL, Tk_Offset(Htext, yScrollCmd), TK_CONFIG_NULL_OK},
  417.     {TK_CONFIG_PIXELS, "-yscrollunits", "yScrollUnits", "yScrollUnits",
  418.     DEF_HTEXT_SCROLL_UNITS, Tk_Offset(Htext, scrollY), 
  419.         TK_CONFIG_DONT_SET_DEFAULT},
  420.     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
  421.     (char *)NULL, 0, 0}
  422. };
  423.  
  424. /* Forward Declarations */
  425.  
  426. static int TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
  427.     Tcl_Interp *interp, int argc, char **argv));
  428. static void DestroyText _ANSI_ARGS_((ClientData clientdata));
  429. static int ConfigureHtext _ANSI_ARGS_((Tcl_Interp *interp,
  430.     Htext *textPtr, int argc, char **argv, int flags));
  431. static void TextEventProc _ANSI_ARGS_((ClientData clientdata,
  432.     XEvent *eventPtr));
  433. static void EventuallyRedraw _ANSI_ARGS_((Htext *textPtr));
  434. static int IncludeText _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
  435. static int ParseInput _ANSI_ARGS_((Tcl_Interp *, Htext *, char *));
  436. static char *ReadFile _ANSI_ARGS_((Tcl_Interp *interp, char *fileName));
  437. static int AppendChild _ANSI_ARGS_((Htext *, Tcl_Interp *, int, char **));
  438. static Child *CreateChild _ANSI_ARGS_((Htext *textPtr, char *pathName));
  439. static void DestroyChild _ANSI_ARGS_((Child *childPtr));
  440. static void ChildStructureProc _ANSI_ARGS_((ClientData clientdata,
  441.     XEvent *eventPtr));
  442. static Child *FindChild _ANSI_ARGS_((Htext *textPtr, char *childName));
  443. static char *CollectCommand _ANSI_ARGS_((Htext *textPtr, char *inputPtr,
  444.     char *command));
  445. static Line *NewLine _ANSI_ARGS_((Htext *textPtr));
  446. static void DestroyLine _ANSI_ARGS_((Line *linePtr));
  447. static Line *GetLastLine _ANSI_ARGS_((Htext *textPtr));
  448. static void SetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int size));
  449. static void GetTextInfo _ANSI_ARGS_((Line *linePtr, char *line, int *size));
  450. static void ComputeLayout _ANSI_ARGS_((Htext *textPtr));
  451. static void LayoutLine _ANSI_ARGS_((Htext *textPtr, Line *linePtr));
  452. static void FreeAllLines _ANSI_ARGS_((Htext *textPtr));
  453. static void AdjustLinesAllocated _ANSI_ARGS_((Htext *textPtr));
  454. static void DisplayText _ANSI_ARGS_((ClientData clientData));
  455. static void DrawPage _ANSI_ARGS_((Htext *textPtr, int deltaY));
  456. static void MoveChild _ANSI_ARGS_((Child *childPtr, int offset));
  457. static void UpdateScrollbar _ANSI_ARGS_((Tcl_Interp *interp, char *cmd,
  458.     int total, int window, int first, int units));
  459. static int GetVisibleLines _ANSI_ARGS_((Htext *textPtr));
  460. static int LineSearch _ANSI_ARGS_((Htext *textPtr, int position,
  461.     int low, int high));
  462. static int ResizeArray _ANSI_ARGS_((char **memPtr, unsigned int elemSize,
  463.     unsigned int oldCount, unsigned int newCount));
  464. static void CreateTraces _ANSI_ARGS_((Htext *textPtr));
  465. static void DeleteTraces _ANSI_ARGS_((Htext *textPtr));
  466. static void ChildGeometryProc _ANSI_ARGS_((ClientData clientData,
  467.     Tk_Window tkwin));
  468.  
  469. static Tk_GeomMgr ChildGeometry = { "BltHtext", ChildGeometryProc, NULL } ;
  470.  
  471. static void SendBogusEvent _ANSI_ARGS_((Tk_Window tkwin));
  472.  
  473. extern void TkDisplayChars _ANSI_ARGS_((Display *display,
  474.                             Drawable drawable, GC gc,
  475.                             XFontStruct *fontStructPtr, char *string,
  476.                             int numChars, int x, int y, int tabOrigin,
  477.                             int flags));
  478. extern int TkMeasureChars _ANSI_ARGS_((XFontStruct *fontStructPtr,
  479.                             char *source, int maxChars, int startX, int maxX,
  480.                             int tabOrigin, int flags, int *nextXPtr));
  481.  
  482. #if (TK_MINOR_VERSION > 0)
  483. extern int Blt_OptionChanged _ANSI_ARGS_(TCL_VARARGS(Tk_ConfigSpec *,specs));
  484. #else
  485. extern int Blt_OptionChanged _ANSI_ARGS_(VARARGS(Tk_ConfigSpec *specs));
  486. #endif
  487.  
  488. /* end of Forward Declarations */
  489.  
  490. /*
  491.  *----------------------------------------------------------------------
  492.  *
  493.  * ParseFill --
  494.  *
  495.  *    Converts the fill style string into its numeric representation.
  496.  *    This configuration option affects how the slave window is expanded
  497.  *    if there is extra space in the cubicle in which it sits.
  498.  *
  499.  *    Valid style strings are:
  500.  *
  501.  *        "none"   Don't expand the window to fill the cubicle.
  502.  *         "x"         Expand only the window's width.
  503.  *        "y"         Expand only the window's height.
  504.  *        "both"   Expand both the window's height and width.
  505.  *
  506.  *----------------------------------------------------------------------
  507.  */
  508. /*ARGSUSED*/
  509. static int
  510. ParseFill(clientData, interp, tkwin, value, widgRec, offset)
  511.     ClientData clientData;    /* not used */
  512.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  513.     Tk_Window tkwin;        /* not used */
  514.     char *value;        /* Fill style string */
  515.     char *widgRec;        /* Cubicle structure record */
  516.     int offset;            /* Offset of style in record */
  517. {
  518.     FillFlags *fillPtr = (FillFlags *)(widgRec + offset);
  519.     int length;
  520.     char c;
  521.  
  522.     c = value[0];
  523.     length = strlen(value);
  524.     if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
  525.     *fillPtr = FILL_NONE;
  526.     } else if ((c == 'x') && (strncmp(value, "x", length) == 0)) {
  527.     *fillPtr = FILL_X;
  528.     } else if ((c == 'y') && (strncmp(value, "y", length) == 0)) {
  529.     *fillPtr = FILL_Y;
  530.     } else if ((c == 'b') && (strncmp(value, "both", length) == 0)) {
  531.     *fillPtr = FILL_BOTH;
  532.     } else {
  533.     Tcl_AppendResult(interp, "bad fill argument \"", value,
  534.         "\": should be none, x, y, or both", (char *)NULL);
  535.     return TCL_ERROR;
  536.     }
  537.     return (TCL_OK);
  538. }
  539.  
  540. /*
  541.  *----------------------------------------------------------------------
  542.  *
  543.  * PrintFill --
  544.  *
  545.  *    Returns the fill style string based upon the fill flags.
  546.  *
  547.  * Results:
  548.  *    The fill style string is returned.
  549.  *
  550.  *----------------------------------------------------------------------
  551.  */
  552. /*ARGSUSED*/
  553. static char *
  554. PrintFill(clientData, tkwin, widgRec, offset, freeProcPtr)
  555.     ClientData clientData;    /* not used */
  556.     Tk_Window tkwin;        /* not used */
  557.     char *widgRec;        /* Row/column structure record */
  558.     int offset;            /* Offset of fill in Partition record */
  559.     Tcl_FreeProc **freeProcPtr;    /* not used */
  560. {
  561.     FillFlags fill = *(FillFlags *)(widgRec + offset);
  562.  
  563.     return (fillStrings[(int)fill]);
  564. }
  565.  
  566. /*
  567.  *----------------------------------------------------------------------
  568.  *
  569.  * ParseJustify --
  570.  *
  571.  *     Converts the justification string into its numeric
  572.  *     representation. This configuration option affects how the
  573.  *    slave window is positioned with respect to the line on which
  574.  *    it sits.
  575.  *
  576.  *    Valid style strings are:
  577.  *
  578.  *    "top"      Uppermost point of region is top of the line's
  579.  *           text
  580.  *     "center"   Center point of region is line's baseline.
  581.  *    "bottom"   Lowermost point of region is bottom of the
  582.  *           line's text
  583.  *
  584.  * Returns:
  585.  *    A standard Tcl result.  If the value was not valid
  586.  *
  587.  *---------------------------------------------------------------------- */
  588. /*ARGSUSED*/
  589. static int
  590. ParseJustify(clientData, interp, tkwin, value, widgRec, offset)
  591.     ClientData clientData;    /* not used */
  592.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  593.     Tk_Window tkwin;        /* not used */
  594.     char *value;        /* Justification string */
  595.     char *widgRec;        /* Structure record */
  596.     int offset;            /* Offset of justify in record */
  597. {
  598.     Justify *justPtr = (Justify *)(widgRec + offset);
  599.     int length;
  600.     char c;
  601.  
  602.     c = value[0];
  603.     length = strlen(value);
  604.     if ((c == 'c') && (strncmp(value, "center", length) == 0)) {
  605.     *justPtr = JUSTIFY_CENTER;
  606.     } else if ((c == 't') && (strncmp(value, "top", length) == 0)) {
  607.     *justPtr = JUSTIFY_TOP;
  608.     } else if ((c == 'b') && (strncmp(value, "bottom", length) == 0)) {
  609.     *justPtr = JUSTIFY_BOTTOM;
  610.     } else {
  611.     Tcl_AppendResult(interp, "bad justification argument \"", value,
  612.         "\": should be center, top, or bottom", (char *)NULL);
  613.     return TCL_ERROR;
  614.     }
  615.     return (TCL_OK);
  616. }
  617.  
  618. /*
  619.  *----------------------------------------------------------------------
  620.  *
  621.  * PrintJustify --
  622.  *
  623.  *    Returns the justification style string based upon the value.
  624.  *
  625.  * Results:
  626.  *    The justification style string is returned.
  627.  *
  628.  *----------------------------------------------------------------------
  629.  */
  630. /*ARGSUSED*/
  631. static char *
  632. PrintJustify(clientData, tkwin, widgRec, offset, freeProcPtr)
  633.     ClientData clientData;    /* not used */
  634.     Tk_Window tkwin;        /* not used */
  635.     char *widgRec;        /* Structure record */
  636.     int offset;            /* Offset of justify record */
  637.     Tcl_FreeProc **freeProcPtr;    /* not used */
  638. {
  639.     Justify justify = *(Justify *)(widgRec + offset);
  640.  
  641.     return (justifyStrings[(int)justify]);
  642. }
  643.  
  644. /*
  645.  *----------------------------------------------------------------------
  646.  *
  647.  * EventuallyRedraw --
  648.  *
  649.  *    Queues a request to redraw the text window at the next idle
  650.  *    point.
  651.  *
  652.  * Results:
  653.  *    None.
  654.  *
  655.  * Side effects:
  656.  *    Information gets redisplayed.  Right now we don't do selective
  657.  *    redisplays:  the whole window will be redrawn.  This doesn't
  658.  *    seem to hurt performance noticeably, but if it does then this
  659.  *    could be changed.
  660.  *
  661.  *----------------------------------------------------------------------
  662.  */
  663. static void
  664. EventuallyRedraw(textPtr)
  665.     register Htext *textPtr;    /* Information about widget. */
  666. {
  667.     if ((textPtr->tkwin != NULL) && !(textPtr->flags & REDRAW_PENDING)) {
  668.     textPtr->flags |= REDRAW_PENDING;
  669.     Tk_DoWhenIdle(DisplayText, (ClientData)textPtr);
  670.     }
  671. }
  672.  
  673. /*
  674.  * ----------------------------------------------------------------------
  675.  *
  676.  * DestroyText --
  677.  *
  678.  *     This procedure is invoked by Tk_EventuallyFree or Tk_Release
  679.  *    to clean up the internal structure of a Htext at a safe time
  680.  *    (when no-one is using it anymore).
  681.  *
  682.  * Results:
  683.  *    None.
  684.  *
  685.  * Side effects:
  686.  *    Everything associated with the widget is freed up.
  687.  *
  688.  * ----------------------------------------------------------------------
  689.  */
  690.  
  691. static void
  692. DestroyText(clientData)
  693.     ClientData clientData;    /* Info about hypertext widget. */
  694. {
  695.     register Htext *textPtr = (Htext *)clientData;
  696.  
  697.     if (textPtr->gc != NULL) {
  698.     Tk_FreeGC(textPtr->display, textPtr->gc);
  699.     }
  700.     Tk_FreeOptions(configSpecs, (char *)textPtr, textPtr->display, 0);
  701.     FreeAllLines(textPtr);
  702.     Tcl_DeleteHashTable(&(textPtr->subwindows));
  703.     free((char *)textPtr);
  704. }
  705.  
  706. /*
  707.  * --------------------------------------------------------------
  708.  *
  709.  * TextEventProc --
  710.  *
  711.  *     This procedure is invoked by the Tk dispatcher for various
  712.  *     events on hypertext widgets.
  713.  *
  714.  * Results:
  715.  *    None.
  716.  *
  717.  * Side effects:
  718.  *    When the window gets deleted, internal structures get
  719.  *    cleaned up.  When it gets exposed, it is redisplayed.
  720.  *
  721.  * --------------------------------------------------------------
  722.  */
  723.  
  724. static void
  725. TextEventProc(clientData, eventPtr)
  726.     ClientData clientData;    /* Information about window. */
  727.     XEvent *eventPtr;        /* Information about event. */
  728. {
  729.     Htext *textPtr = (Htext *)clientData;
  730.  
  731.     if (eventPtr->type == ConfigureNotify) {
  732.     if ((textPtr->width != Tk_Width(textPtr->tkwin)) ||
  733.         (textPtr->height != Tk_Height(textPtr->tkwin))) {
  734.         textPtr->flags |= (REQUEST_LAYOUT | VIEW_RESIZED);
  735.         EventuallyRedraw(textPtr);
  736.     }
  737.     } else if (eventPtr->type == Expose) {
  738.  
  739.     /*
  740.      * If the Expose event was synthetic (i.e. we manufactured it
  741.      * ourselves during a redraw operation), toggle the bit flag
  742.      * which controls redraws.
  743.      */
  744.  
  745.     if (eventPtr->xexpose.send_event) {
  746.         textPtr->flags ^= IGNORE_EXPOSURES;
  747.         return;
  748.     }
  749.     if ((eventPtr->xexpose.count == 0) &&
  750.         !(textPtr->flags & IGNORE_EXPOSURES)) {
  751.         EventuallyRedraw(textPtr);
  752.     }
  753.     } else if (eventPtr->type == DestroyNotify) {
  754.     Tcl_DeleteCommand(textPtr->interp, Tk_PathName(textPtr->tkwin));
  755.     textPtr->tkwin = NULL;
  756.     if (textPtr->flags & REDRAW_PENDING) {
  757.         Tk_CancelIdleCall(DisplayText, (ClientData)textPtr);
  758.     }
  759. #if (TK_MINOR_VERSION > 0)
  760.     Tk_EventuallyFree((ClientData)textPtr, (Tcl_FreeProc *)DestroyText);
  761. #else
  762.     Tk_EventuallyFree((ClientData)textPtr, (Tk_FreeProc *)DestroyText);
  763. #endif
  764.     }
  765. }
  766.  
  767. /*
  768.  * ----------------------------------------------------------------------
  769.  *
  770.  * ConfigureHtext --
  771.  *
  772.  *     This procedure is called to process an argv/argc list, plus
  773.  *    the Tk option database, in order to configure (or reconfigure)
  774.  *    a hypertext widget.
  775.  *
  776.  *     The layout of the text must be calculated (by ComputeLayout)
  777.  *    whenever particular options change; -font, -filename, -linespacing
  778.  *    and -text options. If the user has changes one of these options,
  779.  *    it must be detected so that the layout can be recomputed. Since the
  780.  *    coordinates of the layout are virtual, there is no need to adjust
  781.  *    them if physical window attributes (window size, etc.)
  782.  *    change.
  783.  *
  784.  * Results:
  785.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  786.  *     returned, then interp->result contains an error message.
  787.  *
  788.  * Side effects:
  789.  *    Configuration information, such as text string, colors, font,
  790.  *     etc. get set for textPtr;  old resources get freed, if there were any.
  791.  *     The hypertext is redisplayed.
  792.  *
  793.  * ----------------------------------------------------------------------
  794.  */
  795.  
  796.  
  797. static int
  798. ConfigureHtext(interp, textPtr, argc, argv, flags)
  799.     Tcl_Interp *interp;        /* Used for error reporting. */
  800.     Htext *textPtr;        /* Information about widget; may or may not
  801.                      * already have values for some fields. */
  802.     int argc;            /* Number of valid entries in argv. */
  803.     char **argv;        /* Arguments. */
  804.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  805. {
  806.     XGCValues gcValues;
  807.     unsigned long valueMask;
  808.     GC newGC;
  809.  
  810.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs, argc, argv,
  811.         (char *)textPtr, flags) != TCL_OK) {
  812.     return TCL_ERROR;
  813.     }
  814.     Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
  815.     if (Blt_OptionChanged(configSpecs, "-font", "-linespacing", (char *)NULL)) {
  816.     textPtr->flags |= REQUEST_LAYOUT;
  817.     }
  818.     gcValues.font = textPtr->fontPtr->fid;
  819.     gcValues.foreground = textPtr->normalFg->pixel;
  820.     valueMask = GCForeground | GCFont;
  821.     newGC = Tk_GetGC(textPtr->tkwin, valueMask, &gcValues);
  822.     if (textPtr->gc != NULL) {
  823.     Tk_FreeGC(textPtr->display, textPtr->gc);
  824.     }
  825.     textPtr->gc = newGC;
  826.  
  827.     /*
  828.      * If the either the -text or -file option changed, read in the
  829.      * new text.  The -text option supersedes any -file option.
  830.      */
  831.     if (Blt_OptionChanged(configSpecs, "-filename", "-text", (char *)NULL)) {
  832.     int result;
  833.  
  834.     FreeAllLines(textPtr);    /* Delete any previous lines */
  835.     CreateTraces(textPtr);    /* Create variable traces */
  836.  
  837.     result = IncludeText(interp, textPtr, textPtr->fileName);
  838.  
  839.     DeleteTraces(textPtr);
  840.     if (result == TCL_ERROR) {
  841.         FreeAllLines(textPtr);
  842.         return TCL_ERROR;
  843.     }
  844.     AdjustLinesAllocated(textPtr);
  845.     textPtr->flags |= REQUEST_LAYOUT;    /* Mark for layout update */
  846.     }
  847.     EventuallyRedraw(textPtr);
  848.     return TCL_OK;
  849. }
  850.  
  851. /*
  852.  * ----------------------------------------------------------------------
  853.  *
  854.  * NewLine --
  855.  *
  856.  *     This procedure creates and initializes a new line of text.
  857.  *
  858.  * Results:
  859.  *    The return value is a pointer to a structure describing the new
  860.  *     line of text.  If an error occurred, then the return value is NULL
  861.  *    and an error message is left in interp->result.
  862.  *
  863.  * Side effects:
  864.  *    Memory is allocated.
  865.  *
  866.  * ----------------------------------------------------------------------
  867.  */
  868. static Line *
  869. NewLine(textPtr)
  870.     Htext *textPtr;
  871. {
  872.     Line *linePtr;
  873.  
  874.     if (textPtr->numLines >= textPtr->arraySize) {
  875.     if (textPtr->arraySize == 0) {
  876.         textPtr->arraySize = LINES_ALLOC_CHUNK;
  877.     } else {
  878.         textPtr->arraySize += textPtr->arraySize;
  879.     }
  880.     ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
  881.         textPtr->arraySize, textPtr->numLines);
  882.     }
  883.     /* Create new line entry and add to table */
  884.     linePtr = (Line *)calloc(1, sizeof(Line));
  885.     if (linePtr == NULL) {
  886.     textPtr->interp->result = "can't allocate line structure";
  887.     return NULL;
  888.     }
  889.     Blt_InitLinkedList(&(linePtr->windowList), TCL_ONE_WORD_KEYS);
  890.     textPtr->linePtrPtr[textPtr->numLines++] = linePtr;
  891.     return (linePtr);
  892. }
  893.  
  894. /*
  895.  * ----------------------------------------------------------------------
  896.  *
  897.  * DestroyLine --
  898.  *
  899.  *     This procedure is invoked by FreeAllLines to clean up the
  900.  *     internal structure of a line.
  901.  *
  902.  * Results: None.
  903.  *
  904.  * Side effects:
  905.  *    Everything associated with the line (text and children) is
  906.  *    freed up.
  907.  *
  908.  * ----------------------------------------------------------------------
  909.  */
  910. static void
  911. DestroyLine(linePtr)
  912.     register Line *linePtr;
  913. {
  914.     Blt_ListEntry *entryPtr;
  915.     Child *childPtr;
  916.  
  917.     /* Free the list of child structures */
  918.  
  919.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  920.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  921.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  922.     DestroyChild(childPtr);
  923.     }
  924.     Blt_ClearList(&(linePtr->windowList));
  925.     if (linePtr->text != NULL) {
  926.     free(linePtr->text);
  927.     }
  928.     free((char *)linePtr);
  929. }
  930.  
  931. /*
  932.  * ----------------------------------------------------------------------
  933.  *
  934.  * AppendChild --
  935.  *
  936.  *     This procedure creates and initializes a new hyper text child.
  937.  *
  938.  * Results:
  939.  *    The return value is a standard Tcl result.
  940.  *
  941.  * Side effects:
  942.  *    Memory is allocated.  Child gets configured.
  943.  *
  944.  * ----------------------------------------------------------------------
  945.  */
  946. static int
  947. AppendChild(textPtr, interp, argc, argv)
  948.     Htext *textPtr;        /* Hypertext widget */
  949.     Tcl_Interp *interp;        /* Interpreter associated with widget */
  950.     int argc;            /* Number of arguments. */
  951.     char **argv;        /* Argument strings. */
  952. {
  953.     Line *linePtr;
  954.     Child *childPtr;
  955.     Blt_ListEntry *entryPtr;
  956.  
  957.     if (argc < 3) {
  958.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  959.         " append pathName ?options?\"", (char *)NULL);
  960.     return TCL_ERROR;
  961.     }
  962.     childPtr = CreateChild(textPtr, argv[2]);
  963.     if (childPtr == NULL) {
  964.     return TCL_ERROR;
  965.     }
  966.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
  967.         argc - 3, argv + 3, (char *)childPtr, 0) != TCL_OK) {
  968.     return TCL_ERROR;
  969.     }
  970.     /*
  971.      * Append child to list of subwindows of the last line.
  972.      */
  973.     linePtr = GetLastLine(textPtr);
  974.     entryPtr = Blt_CreateListEntry((char *)childPtr->tkwin);
  975.     Blt_LinkListAfter(&(linePtr->windowList), entryPtr,
  976.     (Blt_ListEntry *)NULL);
  977.     Blt_SetListValue(entryPtr, childPtr);
  978.     linePtr->width += childPtr->cavityWidth;
  979.     childPtr->precedingTextEnd = linePtr->numChars;
  980.  
  981.     textPtr->flags |= REQUEST_LAYOUT;
  982.     EventuallyRedraw(textPtr);
  983.     return TCL_OK;
  984. }
  985.  
  986. /*
  987.  * -----------------------------------------------------------------
  988.  *
  989.  * TranslateAnchor --
  990.  *
  991.  *     Translate the coordinates of a given bounding box based
  992.  *    upon the anchor specified.  The anchor indicates where
  993.  *    the given xy position is in relation to the bounding box.
  994.  *
  995.  *          nw --- n --- ne
  996.  *          |            |     x,y ---+
  997.  *          w   center   e      |     |
  998.  *          |            |      +-----+
  999.  *          sw --- s --- se
  1000.  *
  1001.  * Results:
  1002.  *    The translated coordinates of the bounding box are returned.
  1003.  *
  1004.  * -----------------------------------------------------------------
  1005.  */
  1006. static XPoint
  1007. TranslateAnchor(deltaX, deltaY, anchor)
  1008.     int deltaX, deltaY;        /* Difference between outer and inner regions
  1009.                  */
  1010.     Tk_Anchor anchor;        /* Direction of the anchor */
  1011. {
  1012.     XPoint anchorPos;
  1013.  
  1014.     anchorPos.x = anchorPos.y = 0;
  1015.     switch (anchor) {
  1016.     case TK_ANCHOR_NW:        /* Upper left corner */
  1017.     break;
  1018.     case TK_ANCHOR_W:        /* Left center */
  1019.     anchorPos.y = (deltaY / 2);
  1020.     break;
  1021.     case TK_ANCHOR_SW:        /* Lower left corner */
  1022.     anchorPos.y = deltaY;
  1023.     break;
  1024.     case TK_ANCHOR_N:        /* Top center */
  1025.     anchorPos.x = (deltaX / 2);
  1026.     break;
  1027.     case TK_ANCHOR_CENTER:    /* Centered */
  1028.     anchorPos.x = (deltaX / 2);
  1029.     anchorPos.y = (deltaY / 2);
  1030.     break;
  1031.     case TK_ANCHOR_S:        /* Bottom center */
  1032.     anchorPos.x = (deltaX / 2);
  1033.     anchorPos.y = deltaY;
  1034.     break;
  1035.     case TK_ANCHOR_NE:        /* Upper right corner */
  1036.     anchorPos.x = deltaX;
  1037.     break;
  1038.     case TK_ANCHOR_E:        /* Right center */
  1039.     anchorPos.x = deltaX;
  1040.     anchorPos.y = (deltaY / 2);
  1041.     break;
  1042.     case TK_ANCHOR_SE:        /* Lower right corner */
  1043.     anchorPos.x = deltaX;
  1044.     anchorPos.y = deltaY;
  1045.     break;
  1046.     }
  1047.     return (anchorPos);
  1048. }
  1049.  
  1050. /*
  1051.  * ----------------------------------------------------------------------
  1052.  *
  1053.  * CreateChild --
  1054.  *
  1055.  *     This procedure creates and initializes a new child subwindow
  1056.  *    in the hyper text widget.
  1057.  *
  1058.  * Results:
  1059.  *    The return value is a pointer to a structure describing the
  1060.  *    new child.  If an error occurred, then the return value is
  1061.  *      NULL and an error message is left in interp->result.
  1062.  *
  1063.  * Side effects:
  1064.  *    Memory is allocated. Child window is mapped. Callbacks are set
  1065.  *    up for subwindow resizes and geometry requests.
  1066.  *
  1067.  * ----------------------------------------------------------------------
  1068.  */
  1069. static Child *
  1070. CreateChild(textPtr, name)
  1071.     Htext *textPtr;        /* Hypertext widget */
  1072.     char *name;            /* Name of child window */
  1073. {
  1074.     register Child *childPtr;
  1075.     Tk_Window tkwin;
  1076.     char buf[BUFSIZ];
  1077.     Tcl_HashEntry *entryPtr;
  1078.     int dummy;
  1079.  
  1080.     if (name[0] != '.') {    /* Relative path, make absolute */
  1081.     sprintf(buf, "%s.%s", Tk_PathName(textPtr->tkwin), name);
  1082.     name = buf;
  1083.     }
  1084.     /* Get the Tk window and parent Tk window associated with the child */
  1085.     tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
  1086.     if (tkwin == NULL) {
  1087.     return NULL;
  1088.     }
  1089.     if (FindChild(textPtr, name) != NULL) {
  1090.     Tcl_AppendResult(textPtr->interp, "\"", name,
  1091.         "\" is already appended to ", Tk_PathName(textPtr->tkwin),
  1092.         (char *)NULL);
  1093.     return NULL;
  1094.     }
  1095.     if (textPtr->tkwin != Tk_Parent(tkwin)) {
  1096.     Tcl_AppendResult(textPtr->interp, "\"", name,
  1097.         "\" is not a child of ", Tk_PathName(textPtr->tkwin),
  1098.         (char *)NULL);
  1099.     return NULL;
  1100.     }
  1101.     childPtr = (Child *)calloc(1, sizeof(Child));
  1102.     if (childPtr == NULL) {
  1103.     textPtr->interp->result = "can't create child structure";
  1104.     return NULL;
  1105.     }
  1106.     childPtr->tkwin = tkwin;
  1107.     childPtr->textPtr = textPtr;
  1108.     childPtr->x = childPtr->y = 0;
  1109.     childPtr->fill = FILL_NONE;
  1110.     childPtr->justify = JUSTIFY_CENTER;
  1111.     childPtr->anchor = TK_ANCHOR_CENTER;
  1112.     entryPtr = Tcl_CreateHashEntry(&(textPtr->subwindows), (char *)tkwin,
  1113.     &dummy);
  1114.     Tcl_SetHashValue(entryPtr, (ClientData)childPtr);
  1115.  
  1116.     Tk_ManageGeometry(tkwin, &ChildGeometry, (ClientData)childPtr);
  1117.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, ChildStructureProc,
  1118.     (ClientData)childPtr);
  1119.     return (childPtr);
  1120. }
  1121.  
  1122. /*
  1123.  * ----------------------------------------------------------------------
  1124.  *
  1125.  * DestroyChild --
  1126.  *
  1127.  *     This procedure is invoked by DestroyLine to clean up the
  1128.  *     internal structure of a child.
  1129.  *
  1130.  * Results:
  1131.  *    None.
  1132.  *
  1133.  * Side effects:
  1134.  *    Everything associated with the widget is freed up.
  1135.  *
  1136.  * ----------------------------------------------------------------------
  1137.  */
  1138. static void
  1139. DestroyChild(childPtr)
  1140.     register Child *childPtr;
  1141. {
  1142.     /* Destroy the child window if it still exists */
  1143.     if (childPtr->tkwin != NULL) {
  1144.     Tcl_HashEntry *entryPtr;
  1145.  
  1146.     Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
  1147.         ChildStructureProc, (ClientData)childPtr);
  1148.     entryPtr = Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
  1149.         (char *)childPtr->tkwin);
  1150.     Tcl_DeleteHashEntry(entryPtr);
  1151.     Tk_DestroyWindow(childPtr->tkwin);
  1152.     }
  1153.     free((char *)childPtr);
  1154. }
  1155.  
  1156. /*
  1157.  * --------------------------------------------------------------
  1158.  *
  1159.  * TextEventProc --
  1160.  *
  1161.  *     This procedure is invoked by the Tk dispatcher for various
  1162.  *     events on hypertext widgets.
  1163.  *
  1164.  * Results:
  1165.  *    None.
  1166.  *
  1167.  * Side effects:
  1168.  *    When the window gets deleted, internal structures get
  1169.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1170.  *
  1171.  * --------------------------------------------------------------
  1172.  */
  1173. static void
  1174. ChildStructureProc(clientData, eventPtr)
  1175.     ClientData clientData;    /* Information about window. */
  1176.     XEvent *eventPtr;        /* Information about event. */
  1177. {
  1178.     register Child *childPtr = (Child *)clientData;
  1179.     Htext *textPtr;
  1180.  
  1181.     if ((childPtr == NULL) || (childPtr->tkwin == NULL)) {
  1182.     return;
  1183.     }
  1184.     textPtr = childPtr->textPtr;
  1185.  
  1186.     if (eventPtr->type == DestroyNotify) {
  1187.     /*
  1188.      * Mark the child as deleted by dereferencing the Tk window
  1189.      * pointer. Zero out the height and width to collapse the area
  1190.      * used by the child.  Redraw the window only if the child is
  1191.      * currently visible and mapped.
  1192.      */
  1193.     childPtr->textPtr->flags |= REQUEST_LAYOUT;
  1194.     if (Tk_IsMapped(childPtr->tkwin) && (childPtr->flags & VISIBLE)) {
  1195.         EventuallyRedraw(textPtr);
  1196.     }
  1197.     Tk_DeleteEventHandler(childPtr->tkwin, StructureNotifyMask,
  1198.         ChildStructureProc, (ClientData)childPtr);
  1199.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&(childPtr->textPtr->subwindows),
  1200.         (char *)childPtr->tkwin));
  1201.     childPtr->tkwin = NULL;
  1202.     } else if (eventPtr->type == ConfigureNotify) {
  1203.  
  1204.     /*
  1205.      * Children can't request new positions by themselves, so worry
  1206.      * only about resizing.
  1207.      */
  1208.  
  1209.     if (childPtr->windowWidth != Tk_Width(childPtr->tkwin) ||
  1210.         childPtr->windowHeight != Tk_Height(childPtr->tkwin)) {
  1211.         EventuallyRedraw(textPtr);
  1212.         textPtr->flags |= REQUEST_LAYOUT;
  1213.     }
  1214.     }
  1215. }
  1216.  
  1217. /*
  1218.  *----------------------------------------------------------------------
  1219.  *
  1220.  * ComputeLayout --
  1221.  *
  1222.  *    This procedure computes the total width and height needed
  1223.  *      to contain the text and children from all the lines of text.
  1224.  *      It merely sums the heights and finds the maximum width of
  1225.  *    all the lines.  The width and height are needed for scrolling.
  1226.  *
  1227.  * Results:
  1228.  *    None.
  1229.  *
  1230.  *----------------------------------------------------------------------
  1231.  */
  1232. static void
  1233. ComputeLayout(textPtr)
  1234.     Htext *textPtr;
  1235. {
  1236.     register int count;
  1237.     register Line **linePtrPtr;
  1238.     register int height, width;
  1239.  
  1240.     width = height = 0;
  1241.     linePtrPtr = textPtr->linePtrPtr;
  1242.     for (count = 0; count < textPtr->numLines; count++) {
  1243.     (*linePtrPtr)->offset = height;
  1244.     LayoutLine(textPtr, *linePtrPtr);
  1245.     height += (*linePtrPtr)->height;
  1246.     if ((*linePtrPtr)->width > width) {
  1247.         width = (*linePtrPtr)->width;
  1248.     }
  1249.     linePtrPtr++;
  1250.     }
  1251.     /*
  1252.      * Set changed flag if new layout changed size of virtual text.
  1253.      */
  1254.     if ((height != textPtr->vPort.height) ||
  1255.     (width != textPtr->vPort.width)) {
  1256.     textPtr->flags |= LAYOUT_CHANGED;
  1257.     textPtr->vPort.height = height;
  1258.     textPtr->vPort.width = width;
  1259.     }
  1260. }
  1261.  
  1262. /*
  1263.  *----------------------------------------------------------------------
  1264.  *
  1265.  * GetReqWidth --
  1266.  *
  1267.  *    Returns the width requested by the child window. The requested
  1268.  *    space also includes any internal padding which has been designated
  1269.  *    for this window.
  1270.  *
  1271.  * Results:
  1272.  *    Returns the requested width of the child window.
  1273.  *
  1274.  *----------------------------------------------------------------------
  1275.  */
  1276.  
  1277. static int
  1278. GetReqWidth(childPtr)
  1279.     Child *childPtr;
  1280. {
  1281.     int width;
  1282.  
  1283.     if (childPtr->reqWidth > 0) {
  1284.     width = childPtr->reqWidth;
  1285.     } else if (childPtr->relWidth > 0.0) {
  1286.     width = (int)((double)Tk_Width(childPtr->textPtr->tkwin) *
  1287.         childPtr->relWidth + 0.5);
  1288.     } else {
  1289.     width = Tk_ReqWidth(childPtr->tkwin) + 2 * (childPtr->ipadX);
  1290.     }
  1291.     return (width);
  1292. }
  1293.  
  1294. /*
  1295.  *----------------------------------------------------------------------
  1296.  *
  1297.  * GetReqHeight --
  1298.  *
  1299.  *    Returns the height requested by the child window. The requested
  1300.  *    space also includes any internal padding which has been designated
  1301.  *    for this window.
  1302.  *
  1303.  * Results:
  1304.  *    Returns the requested height of the child window.
  1305.  *
  1306.  *----------------------------------------------------------------------
  1307.  */
  1308. static int
  1309. GetReqHeight(childPtr)
  1310.     Child *childPtr;
  1311. {
  1312.     int height;
  1313.  
  1314.     if (childPtr->reqHeight > 0) {
  1315.     height = childPtr->reqHeight;
  1316.     } else if (childPtr->relHeight > 0.0) {
  1317.     height = (int)((double)Tk_Height(childPtr->textPtr->tkwin) *
  1318.         childPtr->relHeight + 0.5);
  1319.     } else {
  1320.     height = Tk_ReqHeight(childPtr->tkwin) + 2 * (childPtr->ipadY);
  1321.     }
  1322.     return (height);
  1323. }
  1324.  
  1325. /*
  1326.  *----------------------------------------------------------------------
  1327.  *
  1328.  * GetCavityWidth --
  1329.  *
  1330.  *    Returns the width of the cavity based upon the requested size
  1331.  *    of the child window.  The requested space also includes any
  1332.  *    external padding which has been designated for this window.
  1333.  *
  1334.  * Results:
  1335.  *    Returns the requested width of the cavity.
  1336.  *
  1337.  *----------------------------------------------------------------------
  1338.  */
  1339.  
  1340. static int
  1341. GetCavityWidth(childPtr)
  1342.     Child *childPtr;
  1343. {
  1344.     int width;
  1345.  
  1346.     width = GetReqWidth(childPtr) +
  1347.     (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX));
  1348.     return (width);
  1349. }
  1350.  
  1351. /*
  1352.  *----------------------------------------------------------------------
  1353.  *
  1354.  * GetCavityHeight --
  1355.  *
  1356.  *    Returns the height of the cavity based upon the requested size
  1357.  *    of the child window.  The requested space also includes any
  1358.  *    external padding which has been designated for this window.
  1359.  *
  1360.  * Results:
  1361.  *    Returns the requested height of the cavity.
  1362.  *
  1363.  *----------------------------------------------------------------------
  1364.  */
  1365.  
  1366. static int
  1367. GetCavityHeight(childPtr)
  1368.     Child *childPtr;
  1369. {
  1370.     int height;
  1371.  
  1372.     height = GetReqHeight(childPtr) +
  1373.     (2 * (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY));
  1374.     return (height);
  1375. }
  1376.  
  1377. /*
  1378.  *----------------------------------------------------------------------
  1379.  *
  1380.  * LayoutLine --
  1381.  *
  1382.  *    This procedure computes the total width and height needed
  1383.  *      to contain the text and children for a particular line.
  1384.  *      It also calculates the baseline of the text on the line with
  1385.  *    respect to the other children on the line.
  1386.  *
  1387.  * Results:
  1388.  *    None.
  1389.  *
  1390.  *----------------------------------------------------------------------
  1391.  */
  1392. static void
  1393. LayoutLine(textPtr, linePtr)
  1394.     Htext *textPtr;
  1395.     Line *linePtr;
  1396. {
  1397.     register Child *childPtr;
  1398.     int numChars;
  1399.     int maxAscent, maxDescent, maxHeight;
  1400.     int ascent, descent;
  1401.     register int curPos = 0;
  1402.     int median;            /* Difference of font ascent/descent values */
  1403.     Blt_ListEntry *entryPtr;
  1404.     register int x, y;
  1405.     int newX;
  1406.  
  1407.     /*
  1408.      * Pass 1: Determine the maximum ascent (baseline) and descent
  1409.      * needed for the line.  We'll need this for figuring the top,
  1410.      * bottom, and center anchors.
  1411.      */
  1412.     /* Initialize line defaults */
  1413.     maxAscent = textPtr->fontPtr->ascent;
  1414.     maxDescent = textPtr->fontPtr->descent;
  1415.     median = textPtr->fontPtr->ascent - textPtr->fontPtr->descent;
  1416.     ascent = descent = 0;    /* Suppress compiler warnings */
  1417.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1418.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1419.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  1420.     if (childPtr->tkwin == NULL) {
  1421.         continue;
  1422.     }
  1423.     childPtr->cavityHeight = GetCavityHeight(childPtr);
  1424.     childPtr->cavityWidth = GetCavityWidth(childPtr);
  1425.     switch (childPtr->justify) {
  1426.     case JUSTIFY_TOP:
  1427.         ascent = textPtr->fontPtr->ascent + childPtr->padY;
  1428.         descent = childPtr->cavityHeight - textPtr->fontPtr->ascent;
  1429.         break;
  1430.     case JUSTIFY_CENTER:
  1431.         ascent = (childPtr->cavityHeight + median) / 2;
  1432.         descent = (childPtr->cavityHeight - median) / 2;
  1433.         break;
  1434.     case JUSTIFY_BOTTOM:
  1435.         ascent = childPtr->cavityHeight - textPtr->fontPtr->descent;
  1436.         descent = textPtr->fontPtr->descent;
  1437.         break;
  1438.     }
  1439.     if (descent > maxDescent) {
  1440.         maxDescent = descent;
  1441.     }
  1442.     if (ascent > maxAscent) {
  1443.         maxAscent = ascent;
  1444.     }
  1445.     }
  1446.  
  1447.     maxHeight = maxAscent + maxDescent + textPtr->lineSpacing;
  1448.     x = 0;            /* Always starts from x=0 */
  1449.     y = 0;            /* Suppress compiler warning */
  1450.     curPos = 0;
  1451.     /*
  1452.      * Pass 2: Find the placements of the text and children along each
  1453.      * line.
  1454.      */
  1455.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1456.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1457.     childPtr = (Child *)Blt_GetListValue(entryPtr);
  1458.     if (childPtr->tkwin == NULL) {
  1459.         continue;
  1460.     }
  1461.     /* Get the width of the text leading to the child */
  1462.     numChars = (childPtr->precedingTextEnd - curPos);
  1463.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1464.         TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos,
  1465.         numChars, x, 10000, 0, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
  1466.         childPtr->precedingTextWidth = newX - x;
  1467.         x = newX;
  1468.     }
  1469.     switch (childPtr->justify) {
  1470.     case JUSTIFY_TOP:
  1471.         y = maxAscent + textPtr->fontPtr->descent -
  1472.         childPtr->cavityHeight;
  1473.         break;
  1474.     case JUSTIFY_CENTER:
  1475.         y = maxAscent - (childPtr->cavityHeight + median) / 2;
  1476.         break;
  1477.     case JUSTIFY_BOTTOM:
  1478.         y = maxAscent + textPtr->fontPtr->descent -
  1479.         childPtr->cavityHeight;
  1480.         break;
  1481.     }
  1482.     childPtr->x = x;
  1483.     childPtr->y = y;
  1484.     curPos = childPtr->precedingTextEnd;
  1485.     x += childPtr->cavityWidth;
  1486.     }
  1487.  
  1488.     /*
  1489.      * This may be piece of line after last child and will also pick
  1490.      * up the entire line if no children occurred on it
  1491.      */
  1492.     numChars = (linePtr->numChars - curPos);
  1493.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1494.     TkMeasureChars(textPtr->fontPtr, linePtr->text + curPos, numChars,
  1495.         x, 10000, 0, TK_PARTIAL_OK | TK_AT_LEAST_ONE, &newX);
  1496.     x = newX;
  1497.     }
  1498.     /* Update line parameters */
  1499.     linePtr->width = (unsigned int)x;
  1500.     linePtr->height = maxHeight;
  1501.     linePtr->baseline = maxAscent;
  1502. }
  1503.  
  1504. /*
  1505.  * ----------------------------------------------------------------------
  1506.  *
  1507.  * MoveChild --
  1508.  *
  1509.  *     Move a child subwindow to a new location in the hypertext
  1510.  *    parent window.  If the window has no geometry (i.e. width,
  1511.  *    or height is 0), simply unmap to window.
  1512.  *
  1513.  * Results:
  1514.  *    None.
  1515.  *
  1516.  * Side effects:
  1517.  *    Each subwindow is moved to its new location, generating
  1518.  *      Expose events in the parent for each child window moved.
  1519.  *
  1520.  * ----------------------------------------------------------------------
  1521.  */
  1522. static void
  1523. MoveChild(childPtr, offset)
  1524.     register Child *childPtr;
  1525.     int offset;
  1526. {
  1527.     int winWidth, winHeight;
  1528.     int width, height;
  1529.     int deltaX, deltaY;
  1530.     int x, y;
  1531.     int padX, padY;
  1532.  
  1533.     if (childPtr->tkwin == NULL) {
  1534.     return;
  1535.     }
  1536.     winWidth = GetReqWidth(childPtr);
  1537.     winHeight = GetReqHeight(childPtr);
  1538.     if ((winWidth < 1) || (winHeight < 1)) {
  1539.     if (Tk_IsMapped(childPtr->tkwin)) {
  1540.         Tk_UnmapWindow(childPtr->tkwin);
  1541.     }
  1542.     return;
  1543.     }
  1544.     padX = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padX);
  1545.     padY = (Tk_Changes(childPtr->tkwin)->border_width + childPtr->padY);
  1546.     x = padX + childPtr->x - childPtr->textPtr->vPort.x;
  1547.     y = padY + offset + childPtr->y - childPtr->textPtr->vPort.y;
  1548.     width = childPtr->cavityWidth - (2 * padX);
  1549.     height = childPtr->cavityHeight - (2 * padY);
  1550.  
  1551.     if ((width < winWidth) || (childPtr->fill & FILL_X)) {
  1552.     winWidth = width;
  1553.     }
  1554.     if ((height < winHeight) || (childPtr->fill & FILL_Y)) {
  1555.     winHeight = height;
  1556.     }
  1557.     deltaX = deltaY = 0;
  1558.     if (width > winWidth) {
  1559.     deltaX = width - winWidth;
  1560.     }
  1561.     if (height > winHeight) {
  1562.     deltaY = height - winHeight;
  1563.     }
  1564.     if ((deltaX > 0) || (deltaY > 0)) {
  1565.     XPoint anchorPos;
  1566.  
  1567.     anchorPos = TranslateAnchor(deltaX, deltaY, childPtr->anchor);
  1568.     x += anchorPos.x, y += anchorPos.y;
  1569.     }
  1570.     childPtr->windowWidth = winWidth;
  1571.     childPtr->windowHeight = winHeight;
  1572.     if ((x != Tk_X(childPtr->tkwin)) || (y != Tk_Y(childPtr->tkwin)) ||
  1573.     (winWidth != Tk_Width(childPtr->tkwin)) ||
  1574.     (winHeight != Tk_Height(childPtr->tkwin))) {
  1575.     Tk_MoveResizeWindow(childPtr->tkwin, x, y, (unsigned int)winWidth,
  1576.         (unsigned int)winHeight);
  1577.     if (!Tk_IsMapped(childPtr->tkwin)) {
  1578.         Tk_MapWindow(childPtr->tkwin);
  1579.     }
  1580.     }
  1581. }
  1582.  
  1583. /*
  1584.  * ----------------------------------------------------------------------
  1585.  *
  1586.  * DrawPage --
  1587.  *
  1588.  *     This procedure displays the lines of text and moves the child
  1589.  *      windows to their new positions.  It draws lines with regard to
  1590.  *    the direction of the scrolling.  The idea here is to make the
  1591.  *    text and buttons appear to move together. Otherwise you will
  1592.  *    get a "jiggling" effect where the windows appear to bump into
  1593.  *    the next line before that line is moved.  In the worst case, where
  1594.  *    every line has at least one widget, you can get an aquarium effect
  1595.  *      (lines appear to ripple up).
  1596.  *
  1597.  *     The text area may start between line boundaries (to accommodate
  1598.  *    both variable height lines and constant scrolling). Subtract the
  1599.  *    difference of the page offset and the line offset from the starting
  1600.  *    coordinates. For horizontal scrolling, simply subtract the offset
  1601.  *    of the viewport. The window will clip the top of the first line,
  1602.  *    the bottom of the last line, whatever text extends to the left
  1603.  *    or right of the viewport on any line.
  1604.  *
  1605.  * Results:
  1606.  *    None.
  1607.  *
  1608.  * Side effects:
  1609.  *    Commands are output to X to display the line in its current
  1610.  *     mode.
  1611.  *
  1612.  * ----------------------------------------------------------------------
  1613.  */
  1614. static void
  1615. DrawPage(textPtr, deltaY)
  1616.     Htext *textPtr;
  1617.     int deltaY;            /* Change from previous Y coordinate */
  1618. {
  1619.     Line *linePtr;
  1620.     Child *childPtr;
  1621.     Tk_Window tkwin = textPtr->tkwin;
  1622.     int numChars;
  1623.     int curPos;
  1624.     int baseline;
  1625.     Pixmap pixMap;
  1626.     int forceCopy = 0;
  1627.     register int i;
  1628.     int curLine, lastY;
  1629.     register int x, y;
  1630.     Blt_ListEntry *entryPtr;
  1631.  
  1632.     /* Setup: Clear the display */
  1633.     /* Create an off-screen pixmap for semi-smooth scrolling. */
  1634.     pixMap = XCreatePixmap(textPtr->display, Tk_WindowId(tkwin),
  1635.     Tk_Width(tkwin), Tk_Height(tkwin),
  1636.     DefaultDepthOfScreen(Tk_Screen(tkwin)));
  1637.     Tk_Fill3DRectangle(tkwin, pixMap, textPtr->border, 0, 0,
  1638.     Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1639.     x = -(textPtr->vPort.x);
  1640.     y = -(textPtr->vPort.y);
  1641.  
  1642.     if (deltaY >= 0) {
  1643.     y += textPtr->linePtrPtr[textPtr->first]->offset;
  1644.     curLine = textPtr->first;
  1645.     lastY = 0;
  1646.     } else {
  1647.     y += textPtr->linePtrPtr[textPtr->last]->offset;
  1648.     curLine = textPtr->last;
  1649.     lastY = Tk_Height(tkwin);
  1650.     }
  1651.     forceCopy = 0;
  1652.  
  1653.     /* Draw each line */
  1654.     for (i = textPtr->first; i <= textPtr->last; i++) {
  1655.  
  1656.     /* Initialize character position in text buffer to start */
  1657.     curPos = 0;
  1658.     /* Initialize X position */
  1659.     x = -(textPtr->vPort.x);
  1660.  
  1661.     linePtr = textPtr->linePtrPtr[curLine];
  1662.     baseline = y + linePtr->baseline;    /* Base line in window
  1663.                          * coordinates */
  1664.  
  1665.     for (entryPtr = Blt_FirstListEntry(&(linePtr->windowList));
  1666.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1667.         childPtr = (Child *)Blt_GetListValue(entryPtr);
  1668.         MoveChild(childPtr, linePtr->offset);
  1669.         childPtr->flags |= VISIBLE;
  1670.         numChars = (childPtr->precedingTextEnd - curPos);
  1671.         if ((numChars > 0) && (linePtr->text != NULL)) {
  1672.         TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
  1673.             textPtr->fontPtr, linePtr->text + curPos,
  1674.             numChars, x, baseline, 0, 0);
  1675.         x += childPtr->precedingTextWidth;
  1676.         }
  1677.         curPos = childPtr->precedingTextEnd;
  1678.         x += childPtr->cavityWidth;
  1679.         forceCopy++;
  1680.     }
  1681.  
  1682.     /*
  1683.      * This may be the text trailing the last child or the entire
  1684.      * line if no children occur on it.
  1685.      */
  1686.     numChars = (linePtr->numChars - curPos);
  1687.     if ((numChars > 0) && (linePtr->text != NULL)) {
  1688. #ifdef notdef
  1689.         if (textPtr->selectFirst >= curLine &&
  1690.         textPtr->selectLast <= curLine) {
  1691.         Tk_Fill3DRectangle(textPtr->display, pixMap,
  1692.             textPtr->selectBorder, y, 0,
  1693.             numChars, Tk_Height(tkwin),
  1694.             0, textPtr->selectBorderWidth);
  1695.         }
  1696. #endif
  1697.         TkDisplayChars(textPtr->display, pixMap, textPtr->gc,
  1698.         textPtr->fontPtr, linePtr->text + curPos,
  1699.         numChars, x, baseline, 0, 0);
  1700.     }
  1701.     /* Go to the top of the next line */
  1702.     if (deltaY >= 0) {
  1703.         y += textPtr->linePtrPtr[curLine++]->height;
  1704.     }
  1705.     if (forceCopy > 0 && !(textPtr->flags & VIEW_RESIZED)) {
  1706.         if (deltaY >= 0) {
  1707.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1708.             textPtr->gc, 0, lastY, Tk_Width(tkwin), y - lastY,
  1709.             0, lastY);
  1710.         } else {
  1711.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1712.             textPtr->gc, 0, y, Tk_Width(tkwin), lastY - y,
  1713.             0, y);
  1714.         }
  1715.         forceCopy = 0;    /* Reset drawing flag */
  1716.         lastY = y;        /* Record last Y position */
  1717.     }
  1718.     if ((deltaY < 0) && (curLine > 0)) {
  1719.         y -= textPtr->linePtrPtr[--curLine]->height;
  1720.     }
  1721.     }
  1722.     /*
  1723.      * If the viewport was resized, draw the page in one operation.
  1724.      * Otherwise draw any left-over block of text (either at the top
  1725.      * or bottom of the page)
  1726.      */
  1727.     if (textPtr->flags & VIEW_RESIZED) {
  1728.     XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1729.         textPtr->gc, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  1730.     } else if (lastY != y) {
  1731.     if (deltaY >= 0) {
  1732.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1733.         textPtr->gc, 0, lastY, Tk_Width(tkwin),
  1734.         Tk_Height(tkwin) - lastY, 0, lastY);
  1735.     } else {
  1736.         XCopyArea(textPtr->display, pixMap, Tk_WindowId(tkwin),
  1737.         textPtr->gc, 0, 0, Tk_Width(tkwin), lastY, 0, 0);
  1738.     }
  1739.     }
  1740.     XFreePixmap(textPtr->display, pixMap);
  1741. }
  1742.  
  1743. /*
  1744.  * ----------------------------------------------------------------------
  1745.  *
  1746.  * DisplayText --
  1747.  *
  1748.  *     This procedure is invoked to display a hypertext widget.
  1749.  *    Many of the operations which might ordinarily be performed
  1750.  *    elsewhere (e.g. in a configuration routine) are done here
  1751.  *    because of the somewhat unusual interactions occurring between
  1752.  *    the parent and child windows.
  1753.  *
  1754.  *      Recompute the layout of the text if necessary. This is
  1755.  *    necessary if the world coordinate system has changed.
  1756.  *    Specifically, the following may have occurred:
  1757.  *
  1758.  *      -  a text attribute has changed (font, linespacing, etc.).
  1759.  *      -  child option changed (anchor, width, height).
  1760.  *        -  actual child window was resized.
  1761.  *      -  new text string or file.
  1762.  *
  1763.  *      This is deferred to the display routine since potentially
  1764.  *      many of these may occur (especially child window changes).
  1765.  *
  1766.  *    Set the vertical and horizontal scrollbars (if they are
  1767.  *    designated) by issuing a Tcl command.  Done here since
  1768.  *    the text window width and height are needed.
  1769.  *
  1770.  *    If the viewport position or contents have changed in the
  1771.  *    vertical direction,  the now out-of-view child windows
  1772.  *    must be moved off the viewport.  Since child windows will
  1773.  *    obscure the text window, it is imperative that the children
  1774.  *    are moved off before we try to redraw text in the same area.
  1775.  *      This is necessary only for vertical movements.  Horizontal
  1776.  *    child window movements are handled automatically in the
  1777.  *    page drawing routine.
  1778.  *
  1779.  *      Get the new first and last line numbers for the viewport.
  1780.  *      These line numbers may have changed because either a)
  1781.  *      the viewport changed size or position, or b) the text
  1782.  *    (child window sizes or text attributes) have changed.
  1783.  *
  1784.  *    If the viewport has changed vertically (i.e. the first or
  1785.  *      last line numbers have changed), move the now out-of-view
  1786.  *    child windows off the viewport.
  1787.  *
  1788.  *      Potentially many expose events may be generated when the
  1789.  *    the individual child windows are moved and/or resized.
  1790.  *    These events need to be ignored.  Since (I think) expose
  1791.  *     events are guaranteed to happen in order, we can bracket
  1792.  *    them by sending phony events (via XSendEvent). The phony
  1793.  *      event turn on and off flags which indicate if the events
  1794.  *    should be ignored.
  1795.  *
  1796.  *    Finally, the page drawing routine is called.
  1797.  *
  1798.  * Results:
  1799.  *    None.
  1800.  *
  1801.  * Side effects:
  1802.  *     Commands are output to X to display the hypertext in its
  1803.  *    current mode.
  1804.  *
  1805.  * ----------------------------------------------------------------------
  1806.  */
  1807. static void
  1808. DisplayText(clientData)
  1809.     ClientData clientData;    /* Information about widget. */
  1810. {
  1811.     Htext *textPtr = (Htext *)clientData;
  1812.     register Tk_Window tkwin;
  1813.     int oldFirst;        /* First line of old viewport */
  1814.     int oldLast;        /* Last line of old viewport */
  1815.     int deltaY;            /* Change in viewport in Y direction */
  1816.  
  1817.     textPtr->flags &= ~REDRAW_PENDING;
  1818.  
  1819.     tkwin = textPtr->tkwin;
  1820.     if ((tkwin == NULL) || (textPtr->numLines == 0)) {
  1821.     return;
  1822.     }
  1823.     if (textPtr->flags & REQUEST_LAYOUT) {
  1824.     /*
  1825.      * Recompute the layout when children are created, deleted,
  1826.      * moved, or resized.  Also when text attributes (such as
  1827.      * font, linespacing) have changed.
  1828.      */
  1829.     ComputeLayout(textPtr);
  1830.     }
  1831.     textPtr->width = (unsigned int)textPtr->reqWidth;
  1832.     textPtr->height = (unsigned int)textPtr->reqHeight;
  1833.     if (textPtr->width == 0) {
  1834.     textPtr->width = BLT_MIN(textPtr->vPort.width, textPtr->maxWidth);
  1835.     }
  1836.     if (textPtr->height == 0) {
  1837.     textPtr->height = BLT_MIN(textPtr->vPort.height, textPtr->maxHeight);
  1838.     }
  1839.     if ((textPtr->width != Tk_ReqWidth(tkwin)) ||
  1840.     (textPtr->height != Tk_ReqHeight(tkwin))) {
  1841.     Tk_GeometryRequest(tkwin, textPtr->width, textPtr->height);
  1842.     textPtr->flags |= REQUEST_LAYOUT;
  1843.     EventuallyRedraw(textPtr);
  1844.     return;
  1845.     }
  1846.     if (!Tk_IsMapped(tkwin)) {
  1847.     return;
  1848.     }
  1849.     /*
  1850.      * Turn off layout requests here, after the text window has been
  1851.      * mapped Otherwise, relative slave window height and width
  1852.      * requests wrt to the text window will be wrong.
  1853.      */
  1854.     textPtr->flags &= ~REQUEST_LAYOUT;
  1855.  
  1856.     /* Is there a pending gotoline request? */
  1857.     if (textPtr->flags & REQUEST_GOTO) {
  1858.     textPtr->pendingY = textPtr->linePtrPtr[textPtr->reqLineNum]->offset;
  1859.     textPtr->flags &= ~REQUEST_GOTO;
  1860.     }
  1861.     deltaY = textPtr->pendingY - textPtr->vPort.y;
  1862.     oldFirst = textPtr->first, oldLast = textPtr->last;
  1863.  
  1864.     /*
  1865.      * If the viewport has changed size or position, or the text
  1866.      * and/or child subwindows have changed, adjust the scrollbars to
  1867.      * new positions.
  1868.      */
  1869.     if (textPtr->flags & (VIEW_MOVED | VIEW_RESIZED | LAYOUT_CHANGED)) {
  1870.     /* Reset viewport origin and world extents */
  1871.     textPtr->vPort.x = textPtr->pendingX;
  1872.     textPtr->vPort.y = textPtr->pendingY;
  1873.     if (textPtr->xScrollCmd != NULL)
  1874.         UpdateScrollbar(textPtr->interp, textPtr->xScrollCmd,
  1875.         textPtr->vPort.width, Tk_Width(tkwin), textPtr->vPort.x,
  1876.         textPtr->scrollX);
  1877.     if (textPtr->yScrollCmd != NULL)
  1878.         UpdateScrollbar(textPtr->interp, textPtr->yScrollCmd,
  1879.         textPtr->vPort.height, Tk_Height(tkwin),
  1880.         textPtr->vPort.y, textPtr->scrollY);
  1881.     /*
  1882.      * Given a new viewport or text height, find the first and
  1883.      * last line numbers of the new viewport.
  1884.      */
  1885.     GetVisibleLines(textPtr);
  1886.     }
  1887.     /*
  1888.      * (This is a kludge.) Send an expose event before and after
  1889.      * drawing the page of text.  Since moving and resizing of the
  1890.      * subwindows will cause redundant expose events in the parent
  1891.      * window, the phony events will bracket them indicating no action
  1892.      * should be taken.
  1893.      */
  1894.     SendBogusEvent(tkwin);
  1895.  
  1896.     /*
  1897.      * If either the position of the viewport has changed or the size
  1898.      * of width or height of the entire text have changed, move the
  1899.      * children from the previous viewport out of the current
  1900.      * viewport. Worry only about the vertical child window movements.
  1901.      * The horizontal moves are handled by the when drawing the page
  1902.      * of text.
  1903.      */
  1904.     if (textPtr->first != oldFirst || textPtr->last != oldLast) {
  1905.     register Line **linePtrPtr;
  1906.     register int i;
  1907.     int first, last;
  1908.     Blt_ListEntry *entryPtr;
  1909.     Child *childPtr;
  1910.  
  1911.     /* Figure out which lines are now out of the viewport */
  1912.  
  1913.     if ((textPtr->first > oldFirst) && (textPtr->first <= oldLast)) {
  1914.         first = oldFirst, last = textPtr->first;
  1915.     } else if ((textPtr->last < oldLast) && (textPtr->last >= oldFirst)) {
  1916.         first = textPtr->last, last = oldLast;
  1917.     } else {
  1918.         first = oldFirst, last = oldLast;
  1919.     }
  1920.  
  1921.     linePtrPtr = &(textPtr->linePtrPtr[first]);
  1922.     for (i = first; i <= last; i++) {
  1923.         for (entryPtr = Blt_FirstListEntry(&((*linePtrPtr)->windowList));
  1924.         entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  1925.         childPtr = (Child *)Blt_GetListValue(entryPtr);
  1926.         MoveChild(childPtr, (*linePtrPtr)->offset);
  1927.         childPtr->flags &= ~VISIBLE;
  1928.         }
  1929.         linePtrPtr++;
  1930.     }
  1931.     }
  1932.     DrawPage(textPtr, deltaY);
  1933.     SendBogusEvent(tkwin);
  1934.  
  1935.     /* Reset flags */
  1936.     textPtr->flags &= ~(VIEW_RESIZED | VIEW_MOVED | LAYOUT_CHANGED);
  1937. }
  1938.  
  1939. /*
  1940.  * ----------------------------------------------------------------------
  1941.  *
  1942.  * GetVisibleLines --
  1943.  *
  1944.  *     Calculates which lines are visible using the height
  1945.  *      of the viewport and y offset from the top of the text.
  1946.  *
  1947.  * Results:
  1948.  *    None.
  1949.  *
  1950.  * Side effects:
  1951.  *    Only those line between first and last inclusive are
  1952.  *     redrawn.
  1953.  *
  1954.  * ----------------------------------------------------------------------
  1955.  */
  1956. static int
  1957. GetVisibleLines(textPtr)
  1958.     Htext *textPtr;
  1959. {
  1960.     int topLine, bottomLine;
  1961.     int topPos, bottomPos;
  1962.     int lastLine;
  1963.  
  1964.     topPos = textPtr->pendingY;
  1965.     lastLine = textPtr->numLines - 1;
  1966.     /* First line */
  1967.     topLine = LineSearch(textPtr, topPos, 0, lastLine);
  1968.     if (topLine < 0) {
  1969.     /*
  1970.      * This can't be. The newY offset must be corrupted.
  1971.      */
  1972.     fprintf(stderr, "First position not found `%d'", topPos);
  1973.     return TCL_ERROR;
  1974.     }
  1975.     textPtr->first = topLine;
  1976.  
  1977.     /*
  1978.      * If there is less text than window space, the bottom line is the
  1979.      * last line of text.  Otherwise search for the line at the bottom
  1980.      * of the window.
  1981.      */
  1982.     bottomPos = topPos + Tk_Height(textPtr->tkwin) - 1;
  1983.     if (bottomPos < textPtr->vPort.height) {
  1984.     bottomLine = LineSearch(textPtr, bottomPos, topLine, lastLine);
  1985.     } else {
  1986.     bottomLine = lastLine;
  1987.     }
  1988.     if (bottomLine < 0) {
  1989.     /*
  1990.      * This can't be. The newY offset must be corrupted.
  1991.      */
  1992.     fprintf(stderr, "Last position not found `%d'\n", bottomPos);
  1993. #ifdef notdef
  1994.     fprintf(stderr, "vPort.height=%d,height=%d,top=%d,first=%d,last=%d\n",
  1995.         textPtr->vPort.height, Tk_Height(textPtr->tkwin), topPos,
  1996.         textPtr->linePtrPtr[topLine]->offset,
  1997.         textPtr->linePtrPtr[lastLine]->offset);
  1998. #endif
  1999.     return TCL_ERROR;
  2000.     }
  2001.     textPtr->last = bottomLine;
  2002.     return TCL_OK;
  2003. }
  2004.  
  2005. /*
  2006.  * ----------------------------------------------------------------------
  2007.  *
  2008.  * LineSearch --
  2009.  *
  2010.  *     Performs a binary search for the line located at some world
  2011.  *    Y coordinate. The search is limited to those lines between
  2012.  *    low and high inclusive.
  2013.  *
  2014.  * Results:
  2015.  *    Returns the line number at the given Y coordinate. If position
  2016.  *    does not correspond to any of the lines in the given the set,
  2017.  *    -1 is returned.
  2018.  *
  2019.  * ----------------------------------------------------------------------
  2020.  */
  2021. static int
  2022. LineSearch(textPtr, position, low, high)
  2023.     Htext *textPtr;
  2024.     int position;
  2025.     int low, high;
  2026. {
  2027.     register int midPoint;
  2028.     register Line *linePtr;
  2029.  
  2030.     while (low <= high) {
  2031.     midPoint = (low + high) >> 1;
  2032.     linePtr = textPtr->linePtrPtr[midPoint];
  2033.     if (position < linePtr->offset) {
  2034.         high = midPoint - 1;
  2035.     } else if (position >= (linePtr->offset + linePtr->height)) {
  2036.         low = midPoint + 1;
  2037.     } else {
  2038.         return (midPoint);
  2039.     }
  2040.     }
  2041.     return -1;
  2042. }
  2043.  
  2044. /*
  2045.  * ----------------------------------------------------------------------
  2046.  *
  2047.  * UpdateScrollbar --
  2048.  *
  2049.  *     Invoke a Tcl command to the scrollbar, defining the new
  2050.  *    position and length of the scroll. See the Tk documentation
  2051.  *    for further information on the scrollbar.  It is assumed the
  2052.  *    scrollbar command prefix is valid.
  2053.  *
  2054.  * Results:
  2055.  *    None.
  2056.  *
  2057.  * Side Effects:
  2058.  *    Scrollbar is commanded to change position and/or size.
  2059.  *
  2060.  * ----------------------------------------------------------------------
  2061.  */
  2062. static void
  2063. UpdateScrollbar(interp, command, total, window, first, units)
  2064.     Tcl_Interp *interp;
  2065.     char *command;        /* scrollbar command */
  2066.     int total;            /* Total distance */
  2067.     int window;            /* Window distance */
  2068.     int first;            /* Position of viewport */
  2069.     int units;            /* Unit distance */
  2070. {
  2071.     char scrollArgs[1000];
  2072.     int totalUnits, windowUnits;
  2073.     int firstUnit, lastUnit;
  2074.  
  2075.     totalUnits = (total / units) + 1;
  2076.     windowUnits = window / units;
  2077.     firstUnit = first / units;
  2078.     lastUnit = (firstUnit + windowUnits);
  2079.  
  2080.     /* Keep scrolling within range. */
  2081.     if (firstUnit >= totalUnits) {
  2082.     firstUnit = totalUnits;
  2083.     }
  2084.     if (lastUnit > totalUnits) {
  2085.     lastUnit = totalUnits;
  2086.     }
  2087.     sprintf(scrollArgs, " %d %d %d %d", totalUnits, windowUnits,
  2088.     firstUnit, lastUnit);
  2089.     if (Tcl_VarEval(interp, command, scrollArgs, (char *)NULL) != TCL_OK) {
  2090.     Tk_BackgroundError(interp);
  2091.     }
  2092. }
  2093.  
  2094.  
  2095. static void
  2096. FreeAllLines(textPtr)
  2097.     Htext *textPtr;
  2098. {
  2099.     register int i;
  2100.  
  2101.     for (i = 0; i < textPtr->numLines; i++) {
  2102.     DestroyLine(textPtr->linePtrPtr[i]);
  2103.     }
  2104.     if (textPtr->linePtrPtr != NULL) {
  2105.     free((char *)textPtr->linePtrPtr);
  2106.     }
  2107.     textPtr->linePtrPtr = NULL;
  2108.     textPtr->arraySize = textPtr->numLines = 0;
  2109. }
  2110.  
  2111.  
  2112. static void
  2113. AdjustLinesAllocated(textPtr)
  2114.     Htext *textPtr;
  2115. {
  2116.     if (textPtr->arraySize > 0) {
  2117.     ResizeArray((char **)&(textPtr->linePtrPtr), sizeof(Line *),
  2118.         textPtr->numLines, textPtr->arraySize);
  2119.     textPtr->arraySize = textPtr->numLines;
  2120.     textPtr->first = 0;
  2121.     textPtr->last = textPtr->numLines - 1;
  2122.     textPtr->pendingX = textPtr->pendingY = 0;
  2123.     textPtr->vPort.width = textPtr->vPort.height = 0;
  2124.     textPtr->vPort.x = textPtr->vPort.y = 0;
  2125.     }
  2126. }
  2127.  
  2128.  
  2129. static Line *
  2130. GetLastLine(textPtr)
  2131.     Htext *textPtr;
  2132. {
  2133.     Line *linePtr;
  2134.  
  2135.     if (textPtr->numLines == 0) {
  2136.     linePtr = NewLine(textPtr);
  2137.  
  2138.     if (linePtr == NULL) {
  2139.         textPtr->interp->result = "can't allocate line structure";
  2140.         return NULL;
  2141.     }
  2142.     }
  2143.     linePtr = textPtr->linePtrPtr[textPtr->numLines - 1];
  2144.     return linePtr;
  2145. }
  2146.  
  2147. static void
  2148. SetTextInfo(linePtr, text, size)
  2149.     Line *linePtr;
  2150.     char *text;
  2151.     int size;
  2152. {
  2153.     linePtr->numChars = size;
  2154.     if (linePtr->text != NULL) {
  2155.     free((char *)linePtr->text);
  2156.     }
  2157.     linePtr->text = strdup(text);
  2158. }
  2159.  
  2160. static void
  2161. GetTextInfo(linePtr, buffer, sizePtr)
  2162.     Line *linePtr;
  2163.     char *buffer;
  2164.     int *sizePtr;
  2165. {
  2166.     *sizePtr = linePtr->numChars;
  2167.     if (linePtr->numChars > 0) {
  2168.     strcpy(buffer, linePtr->text);
  2169.     }
  2170.     buffer[linePtr->numChars] = 0;
  2171. }
  2172.  
  2173. /*
  2174.  * ----------------------------------------------------------------------
  2175.  *
  2176.  * ParseInput --
  2177.  *
  2178.  *     Parse the input to the Htext structure into an array of lines.
  2179.  *
  2180.  * Results:
  2181.  *    Returns TCL_OK or error depending if the file was read correctly.
  2182.  *
  2183.  * ----------------------------------------------------------------------
  2184.  */
  2185. static int
  2186. ParseInput(interp, textPtr, inputPtr)
  2187.     Tcl_Interp *interp;
  2188.     Htext *textPtr;
  2189.     char *inputPtr;
  2190. {
  2191.     register Line *linePtr;
  2192.     int c;
  2193.  
  2194. #define HUGE_LINE_SIZE 1024
  2195.     char buffer[HUGE_LINE_SIZE];
  2196. #define HUGE_COMMAND_SIZE 10000
  2197.     char command[HUGE_COMMAND_SIZE];
  2198.     int count;
  2199.     register int state;
  2200.  
  2201.     linePtr = GetLastLine(textPtr);
  2202.     if (linePtr == NULL) {
  2203.     return TCL_ERROR;    /* Error allocating line */
  2204.     }
  2205.     GetTextInfo(linePtr, buffer, &count);
  2206.     state = 0;
  2207.     while ((c = *inputPtr++) != '\0') {
  2208.     if (c == textPtr->specChar) {
  2209.         state++;
  2210.     } else if (c == '\n') {
  2211.         state = -1;
  2212.     } else if ((state == 0) && (c == '\\')) {
  2213.         state = 3;
  2214.     } else {
  2215.         state = 0;
  2216.     }
  2217.  
  2218.     switch (state) {
  2219.     case 2:        /* Tcl Command block found */
  2220.         count--;
  2221.         inputPtr = CollectCommand(textPtr, inputPtr, command);
  2222.         if (inputPtr == NULL) {
  2223.         return TCL_ERROR;
  2224.         }
  2225.         linePtr->numChars = count;
  2226.         if (Tcl_Eval(interp, command) != TCL_OK) {
  2227.         return TCL_ERROR;
  2228.         }
  2229.         state = 0;
  2230.         break;
  2231.  
  2232.     case 4:        /* Escaped block designator */
  2233.         buffer[count - 1] = c;
  2234.         state = 0;
  2235.         break;
  2236.  
  2237.     case -1:        /* End of text line  */
  2238.         buffer[count] = '\0';
  2239.         SetTextInfo(linePtr, buffer, count);
  2240.         linePtr = NewLine(textPtr);
  2241.         if (linePtr == NULL) {
  2242.         return TCL_ERROR;
  2243.         }
  2244.         count = state = 0;
  2245.         break;
  2246.  
  2247.     default:        /* Default action, add to text buffer */
  2248.         buffer[count++] = c;
  2249.         break;
  2250.     }
  2251.     if (count == HUGE_LINE_SIZE) {
  2252.         interp->result = "text line is too long";
  2253.         return TCL_ERROR;
  2254.     }
  2255.     }
  2256.     if (count > 0) {
  2257.     buffer[count] = '\0';
  2258.     SetTextInfo(linePtr, buffer, count);
  2259.     }
  2260.     return TCL_OK;
  2261. }
  2262.  
  2263.  
  2264. static char *
  2265. ReadFile(interp, fileName)
  2266.     Tcl_Interp *interp;
  2267.     char *fileName;
  2268. {
  2269.     FILE *fp;
  2270.     int arraySize;
  2271.     register int numBytes;
  2272.     char *buffer;
  2273.     register int count;
  2274.     char *result = NULL;
  2275.  
  2276.     fp = fopen(fileName, "r");
  2277.     if (fp == NULL) {
  2278.     Tcl_AppendResult(interp, "can't open \"", fileName,
  2279.         "\" for reading: ", Tcl_PosixError(interp), (char *)NULL);
  2280.     return NULL;
  2281.     }
  2282.     count = 0;
  2283.     arraySize = BUFSIZ;        /* Initial array size */
  2284.     if (ResizeArray(&buffer, sizeof(char), arraySize, count) != TCL_OK) {
  2285.     interp->result = "can't allocate character array";
  2286.     return NULL;
  2287.     }
  2288.     for (;;) {
  2289.     /* Read in next block of text */
  2290.     numBytes = fread(&buffer[count], sizeof(char), BUFSIZ, fp);
  2291.  
  2292.     if (numBytes < 0) {
  2293.         Tcl_AppendResult(interp, "error reading \"", fileName, "\": ",
  2294.         Tcl_PosixError(interp), (char *)NULL);
  2295.         goto error;
  2296.     } else if (numBytes == 0) {
  2297.         break;
  2298.     }
  2299.     count += numBytes;
  2300.     if (count >= arraySize) {
  2301.         /* Reallocate with double the buffer size */
  2302.         arraySize += arraySize;
  2303.         if (ResizeArray(&buffer, sizeof(char),
  2304.             arraySize, count) != TCL_OK) {
  2305.         interp->result = "can't allocate character array";
  2306.         goto error;
  2307.         }
  2308.     }
  2309.     }
  2310.     buffer[count] = '\0';
  2311.     result = buffer;
  2312.   error:
  2313.     fclose(fp);
  2314.     return result;
  2315. }
  2316.  
  2317.  
  2318. static char *
  2319. CollectCommand(textPtr, inputPtr, buffer)
  2320.     Htext *textPtr;
  2321.     char *inputPtr;
  2322.     char *buffer;
  2323. {
  2324.     register int c;
  2325.     register int state;
  2326.     register int count;
  2327.  
  2328.  
  2329.     /* Simply collect the all the characters until %% into a buffer */
  2330.  
  2331.     state = count = 0;
  2332.     while ((c = *inputPtr++) != '\0') {
  2333.     if (c == textPtr->specChar) {
  2334.         state++;
  2335.     } else if ((state == 0) && (c == '\\')) {
  2336.         state = 3;
  2337.     } else {
  2338.         state = 0;
  2339.     }
  2340.  
  2341.     switch (state) {
  2342.     case 2:        /* End of command block found */
  2343.         buffer[count - 1] = '\0';
  2344.         return (inputPtr);
  2345.  
  2346.     case 4:        /* Escaped block designator */
  2347.         buffer[count] = c;
  2348.         state = 0;
  2349.         break;
  2350.  
  2351.     default:        /* Add to command buffer */
  2352.         buffer[count++] = c;
  2353.         break;
  2354.     }
  2355.     if (count == HUGE_COMMAND_SIZE) {
  2356.         textPtr->interp->result = "command block is too long";
  2357.         return NULL;
  2358.     }
  2359.     }
  2360.     textPtr->interp->result = "premature end of TCL command block";
  2361.     return NULL;
  2362. }
  2363.  
  2364. static int
  2365. IncludeText(interp, textPtr, fileName)
  2366.     Tcl_Interp *interp;
  2367.     Htext *textPtr;
  2368.     char *fileName;
  2369. {
  2370.     char *inputPtr;
  2371.     int result;
  2372.  
  2373.     if ((textPtr->text == NULL) && (fileName == NULL)) {
  2374.     return TCL_OK;        /* Empty text string */
  2375.     }
  2376.     inputPtr = textPtr->text;
  2377.     if (fileName != NULL) {
  2378.     inputPtr = ReadFile(interp, fileName);
  2379.     if (inputPtr == NULL) {
  2380.         return TCL_ERROR;
  2381.     }
  2382.     }
  2383.     result = ParseInput(interp, textPtr, inputPtr);
  2384.     if (fileName != NULL) {
  2385.     free(inputPtr);
  2386.     }
  2387.     return (result);
  2388. }
  2389.  
  2390. #ifdef STk_CODE
  2391. static void CreateTraces(textPtr)
  2392.    Htext *textPtr;
  2393. {
  2394. }
  2395.  
  2396. static void DeleteTraces(textPtr)
  2397.     Htext *textPtr;
  2398. {
  2399. }
  2400.  
  2401. #else
  2402. /* ARGSUSED */
  2403. static char *
  2404. HtextVarProc(clientData, interp, name1, name2, flags)
  2405.     ClientData clientData;    /* Information about widget. */
  2406.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  2407.     char *name1;        /* Name of variable. */
  2408.     char *name2;        /* Second part of variable name. */
  2409.     int flags;            /* Information about what happened. */
  2410. {
  2411.     Htext *textPtr = (Htext *)clientData;
  2412.     Htext *lastTextPtr;
  2413.  
  2414.     /* Check to see of this is the most recent trace */
  2415.     lastTextPtr = (Htext *)Tcl_VarTraceInfo2(interp, name1, name2, flags,
  2416.     HtextVarProc, (ClientData)NULL);
  2417.     if (lastTextPtr != textPtr) {
  2418.     return NULL;        /* Ignore all but most current trace */
  2419.     }
  2420.     if (flags & TCL_TRACE_READS) {
  2421.     char c;
  2422.  
  2423.     c = name2[0];
  2424.     if ((c == 'w') && (strcmp(name2, "widget") == 0)) {
  2425.         Tcl_SetVar2(interp, name1, name2, Tk_PathName(textPtr->tkwin),
  2426.         flags);
  2427.     } else if ((c == 'l') && (strcmp(name2, "line") == 0)) {
  2428.         char buf[80];
  2429.  
  2430.         sprintf(buf, "%d", textPtr->numLines);
  2431.         Tcl_SetVar2(interp, name1, name2, buf, flags);
  2432.     } else if ((c == 'f') && (strcmp(name2, "file") == 0)) {
  2433.         register char *fileName;
  2434.  
  2435.         fileName = textPtr->fileName;
  2436.         if (fileName == NULL) {
  2437.         fileName = "";
  2438.         }
  2439.         Tcl_SetVar2(interp, name1, name2, fileName, flags);
  2440.     } else {
  2441.         return ("Unknown variable");
  2442.     }
  2443.     }
  2444.     return NULL;
  2445. }
  2446.  
  2447. static void
  2448. CreateTraces(textPtr)
  2449.     Htext *textPtr;
  2450. {
  2451.     register Tcl_Interp *interp = textPtr->interp;
  2452.     int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
  2453.     static char globalCmd[] = "global blt_htext";
  2454.  
  2455.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
  2456.     (ClientData)textPtr);
  2457.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
  2458.     (ClientData)textPtr);
  2459.     Tcl_TraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
  2460.     (ClientData)textPtr);
  2461.     /*
  2462.      * Make the traced variables global to the widget
  2463.      */
  2464.     Tcl_Eval(interp, globalCmd);
  2465. }
  2466.  
  2467. static void
  2468. DeleteTraces(textPtr)
  2469.     Htext *textPtr;
  2470. {
  2471.     register Tcl_Interp *interp = textPtr->interp;
  2472.     int flags = (TCL_GLOBAL_ONLY | TCL_TRACE_READS);
  2473.  
  2474.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "file", flags, HtextVarProc,
  2475.     (ClientData)textPtr);
  2476.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "line", flags, HtextVarProc,
  2477.     (ClientData)textPtr);
  2478.     Tcl_UntraceVar2(interp, HTEXT_CMDNAME, "widget", flags, HtextVarProc,
  2479.     (ClientData)textPtr);
  2480.  
  2481. }
  2482. #endif
  2483. /*
  2484.  * ----------------------------------------------------------------------
  2485.  *
  2486.  * FindChild --
  2487.  *
  2488.  *    Searches for a child widget matching the path name given
  2489.  *    If found, the pointer to the child structure is returned,
  2490.  *    otherwise NULL.
  2491.  *
  2492.  * Results:
  2493.  *    The pointer to the child structure. If not found, NULL.
  2494.  *
  2495.  * ----------------------------------------------------------------------
  2496.  */
  2497. static Child *
  2498. FindChild(textPtr, name)
  2499.     Htext *textPtr;        /* Hypertext widget structure */
  2500.     char *name;            /* Path name of child window  */
  2501. {
  2502.     Tcl_HashEntry *entryPtr;
  2503.     Tk_Window tkwin;
  2504.  
  2505.     tkwin = Tk_NameToWindow(textPtr->interp, name, textPtr->tkwin);
  2506.     if (tkwin == NULL) {
  2507.     return NULL;
  2508.     }
  2509.     entryPtr = Tcl_FindHashEntry(&(textPtr->subwindows), (char *)tkwin);
  2510.     if (entryPtr != NULL) {
  2511.     return (Child *) Tcl_GetHashValue(entryPtr);
  2512.     }
  2513.     return NULL;
  2514. }
  2515.  
  2516. /*
  2517.  *--------------------------------------------------------------
  2518.  *
  2519.  * ChildGeometryProc --
  2520.  *
  2521.  *    This procedure is invoked by Tk_GeometryRequest for
  2522.  *    subwindows managed by the hypertext widget.
  2523.  *
  2524.  * Results:
  2525.  *    None.
  2526.  *
  2527.  * Side effects:
  2528.  *    Arranges for tkwin, and all its managed siblings, to
  2529.  *    be repacked and drawn at the next idle point.
  2530.  *
  2531.  *--------------------------------------------------------------
  2532.  */
  2533.  /* ARGSUSED */
  2534. static void
  2535. ChildGeometryProc(clientData, tkwin)
  2536.     ClientData clientData;    /* Information about window that got new
  2537.                      * preferred geometry.  */
  2538.     Tk_Window tkwin;        /* Other Tk-related information about the
  2539.                      * window. */
  2540. {
  2541.     Child *childPtr = (Child *)clientData;
  2542.  
  2543.     if ((childPtr->windowWidth != GetReqWidth(childPtr)) ||
  2544.     (childPtr->windowHeight != GetReqHeight(childPtr))) {
  2545.     childPtr->textPtr->flags |= REQUEST_LAYOUT;
  2546.     EventuallyRedraw(childPtr->textPtr);
  2547.     }
  2548. }
  2549.  
  2550. static void
  2551. SendBogusEvent(tkwin)
  2552.     Tk_Window tkwin;
  2553. {
  2554.     enum {
  2555.     DontPropagate = 0
  2556.     };
  2557.     XEvent event;
  2558.  
  2559.     event.type = event.xexpose.type = Expose;
  2560.     event.xexpose.window = Tk_WindowId(tkwin);
  2561.     event.xexpose.display = Tk_Display(tkwin);
  2562.     event.xexpose.count = 0;
  2563.     event.xexpose.x = event.xexpose.y = 0;
  2564.     event.xexpose.width = Tk_Width(tkwin);
  2565.     event.xexpose.height = Tk_Height(tkwin);
  2566.     XSendEvent(Tk_Display(tkwin), Tk_WindowId(tkwin), DontPropagate,
  2567.     ExposureMask, &event);
  2568. }
  2569.  
  2570. /*
  2571.  * --------------------------------------------------------------------
  2572.  *
  2573.  * ResizeArray --
  2574.  *
  2575.  *    Reallocates memory to the new size given.  New memory
  2576.  *    is also cleared (zeros).
  2577.  *
  2578.  * Results:
  2579.  *    Returns a pointer to the new object or NULL if an error occurred.
  2580.  *
  2581.  * Side Effects:
  2582.  *    Memory is re/allocated.
  2583.  *
  2584.  * --------------------------------------------------------------------
  2585.  */
  2586. static int
  2587. ResizeArray(arrayPtr, elemSize, newLength, prevLength)
  2588.     char **arrayPtr;
  2589.     unsigned int elemSize;
  2590.     unsigned int newLength;
  2591.     unsigned int prevLength;
  2592. {
  2593.     char *newPtr;
  2594.  
  2595.     if (newLength == prevLength) {
  2596.     return TCL_OK;
  2597.     }
  2598.     if (newLength == 0) {    /* Free entire array */
  2599.     free(*arrayPtr);
  2600.     *arrayPtr = NULL;
  2601.     return TCL_OK;
  2602.     }
  2603.     newPtr = (char *)calloc(elemSize, newLength);
  2604.     if (newPtr == NULL) {
  2605.     return TCL_ERROR;
  2606.     }
  2607.     if ((prevLength > 0) && (*arrayPtr != NULL)) {
  2608.     unsigned int size;
  2609.  
  2610.     size = BLT_MIN(prevLength, newLength) * elemSize;
  2611.     if (size > 0) {
  2612.         memcpy(newPtr, *arrayPtr, size);
  2613.     }
  2614.     free(*arrayPtr);
  2615.     }
  2616.     *arrayPtr = newPtr;
  2617.     return TCL_OK;
  2618. }
  2619.  
  2620. #ifdef notdef
  2621. static int
  2622. TextXYToIndex(TextPtr, x, y)
  2623.     Text *TextPtr;
  2624.     int x, y;
  2625.  
  2626. {
  2627.     Line *line = TextPtr->topLine;
  2628.     int result, dummy;
  2629.  
  2630.     /* locate the line */
  2631.  
  2632.     y -= TextPtr->borderWidth + 1;
  2633.  
  2634.     if (y > 0) {
  2635.     int count = y / TextPtr->pmHeight;
  2636.  
  2637.     while (line->length > 0 && count--)
  2638.         line = line->next;
  2639.     }
  2640.     if (line->length == 0)
  2641.     result = TextPtr->numChars;
  2642.     else
  2643.     result = line->index +
  2644.         TkMeasureChars(TextPtr->fontPtr,
  2645.         line->text,
  2646.         line->length,
  2647.         TextPtr->offset,
  2648.         x + TextPtr->leftIndex * TextPtr->avgWidth,
  2649.         0, 0 , &dummy);
  2650.  
  2651.     return result;
  2652. }
  2653.  
  2654. /*
  2655.  *--------------------------------------------------------------
  2656.  *
  2657.  * GetTextIndex --
  2658.  *
  2659.  *    Parse an index into a Text widget and return either its value
  2660.  *    or an error.
  2661.  *
  2662.  * Results:
  2663.  *    A standard Tcl result.  If all went well, then *indexPtr is
  2664.  *    filled in with the index (into TextPtr) corresponding to
  2665.  *    string.  Otherwise an error message is left in interp->result.
  2666.  *
  2667.  * Side effects:
  2668.  *    None.
  2669.  *
  2670.  *--------------------------------------------------------------
  2671.  */
  2672. static int
  2673. GetTextIndex(interp, TextPtr, string, indexPtr)
  2674.     Tcl_Interp *interp;        /* For error messages. */
  2675.     Text *TextPtr;        /* Text for which the index is being
  2676.                  * specified. */
  2677.     char *string;        /* Numerical index into TextPtr's element
  2678.                  * list, or "end" to refer to last element. */
  2679.     int *indexPtr;        /* Where to store converted relief. */
  2680. {
  2681.     int length;
  2682.     char c;
  2683.  
  2684.     length = strlen(string);
  2685.     c = string[0];
  2686.  
  2687.     if ((c == 'e') && (strncmp(string, "end", length) == 0)) {
  2688.     *indexPtr = TextPtr->numChars;
  2689.     } else if ((c == 'c') && (strncmp(string, "cursor", length) == 0)) {
  2690.     *indexPtr = TextPtr->cursorPos;
  2691.     } else if ((c == 's') && (length > 4) &&
  2692.     (strncmp(string, "sel.first", length) == 0)) {
  2693.  
  2694.     if (TextPtr->selectFirst == -1) {
  2695.         interp->result = "selection isn't in Text";
  2696.         return TCL_ERROR;
  2697.     }
  2698.     } else if ((c == 's') && (length > 4) &&
  2699.     (strncmp(string, "sel.last", length) == 0)) {
  2700.     *indexPtr = TextPtr->selectLast;
  2701.     } else if ((c == 't') && (strncmp(string, "top", length) == 0)) {
  2702.     *indexPtr = TextPtr->topLine->index;
  2703.     } else if (c == '@') {
  2704.     char *comma;
  2705.     int x, y;
  2706.  
  2707.     string++;
  2708.     if ((comma = strchr(string, ',')) == NULL) {
  2709.         goto badIndex;
  2710.     }
  2711.     *comma = '\0';
  2712.     if ((Tcl_GetInt(interp, string, &x) != TCL_OK) ||
  2713.         (Tcl_GetInt(interp, comma + 1, &y) != TCL_OK)) {
  2714.         goto badIndex;
  2715.     }
  2716.     if (TextPtr->numChars == 0) {
  2717.         *indexPtr = 0;
  2718.     } else {
  2719.         *indexPtr = TextXYToIndex(TextPtr, x, y);
  2720.     }
  2721.     } else {
  2722.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  2723.         goto badIndex;
  2724.     }
  2725.     if (*indexPtr < 0) {
  2726.         *indexPtr = 0;
  2727.     } else if (*indexPtr > TextPtr->numChars) {
  2728.         *indexPtr = TextPtr->numChars;
  2729.     }
  2730.     }
  2731.     return TCL_OK;
  2732.   badIndex:
  2733.  
  2734.     /*
  2735.      * Some of the paths here leave messages in interp->result, so we
  2736.      * have to clear it out before storing our own message.
  2737.      */
  2738.     Tcl_SetResult(interp, (char *)NULL, TCL_STATIC);
  2739.     Tcl_AppendResult(interp, "bad text index \"", string,
  2740.     "\"", (char *)NULL);
  2741.     return TCL_ERROR;
  2742. }
  2743.  
  2744. /*
  2745.  *----------------------------------------------------------------------
  2746.  *
  2747.  * TextSelectTo --
  2748.  *
  2749.  *    Modify the selection by moving its un-anchored end.  This could
  2750.  *    make the selection either larger or smaller.
  2751.  *
  2752.  * Results:
  2753.  *    None.
  2754.  *
  2755.  * Side effects:
  2756.  *    The selection changes.
  2757.  *
  2758.  *----------------------------------------------------------------------
  2759.  */
  2760. static void
  2761. TextSelectTo(TextPtr, index)
  2762.     register Text *TextPtr;    /* Information about widget. */
  2763.     int index;            /* Index of element that is to
  2764.                  * become the "other" end of the
  2765.                  * selection. */
  2766. {
  2767.     int newFirst, newLast;
  2768.  
  2769.     /*
  2770.      * Grab the selection if we don't own it already.
  2771.      */
  2772.  
  2773.     if (TextPtr->selectFirst == -1) {
  2774.     Tk_OwnSelection(TextPtr->tkwin, TextLostSelection,
  2775.         (ClientData)TextPtr);
  2776.     }
  2777.     if (index < 0) {
  2778.     index = 0;
  2779.     }
  2780.     if (index >= TextPtr->numChars) {
  2781.     index = TextPtr->numChars - 1;
  2782.     }
  2783.     if (TextPtr->selectAnchor > TextPtr->numChars) {
  2784.     TextPtr->selectAnchor = TextPtr->numChars;
  2785.     }
  2786.     if (TextPtr->selectAnchor <= index) {
  2787.     newFirst = TextPtr->selectAnchor;
  2788.     newLast = index;
  2789.     } else {
  2790.     newFirst = index;
  2791.     newLast = TextPtr->selectAnchor - 1;
  2792.     if (newLast < 0) {
  2793.         newFirst = newLast = -1;
  2794.     }
  2795.     }
  2796.     if ((TextPtr->selectFirst == newFirst)
  2797.     && (TextPtr->selectLast == newLast)) {
  2798.     return;
  2799.     }
  2800.     TextPtr->selectFirst = newFirst;
  2801.     TextPtr->selectLast = newLast;
  2802.     TextPtr->flags |= REFRESH_NEEDED;
  2803.     EventuallyRedraw(TextPtr);
  2804. }
  2805.  
  2806. /*
  2807.  *----------------------------------------------------------------------
  2808.  *
  2809.  * TextFetchSelection --
  2810.  *
  2811.  *    This procedure is called back by Tk when the selection is
  2812.  *    requested by someone.  It returns part or all of the selection
  2813.  *    in a buffer provided by the caller.
  2814.  *
  2815.  * Results:
  2816.  *    The return value is the number of non-NULL bytes stored
  2817.  *    at buffer.  Buffer is filled (or partially filled) with a
  2818.  *    NULL-terminated string containing part or all of the selection,
  2819.  *    as given by offset and maxBytes.
  2820.  *
  2821.  * Side effects:
  2822.  *    None.
  2823.  *
  2824.  *----------------------------------------------------------------------
  2825.  */
  2826. static int
  2827. TextFetchSelection(clientData, offset, buffer, maxBytes)
  2828.     ClientData clientData;    /* Information about Text widget. */
  2829.     int offset;            /* Offset within selection of first
  2830.                  * character to be returned. */
  2831.     char *buffer;        /* Location in which to place
  2832.                  * selection. */
  2833.     int maxBytes;        /* Maximum number of bytes to place
  2834.                  * at buffer, not including terminating
  2835.                  * NULL character. */
  2836. {
  2837.     Text *TextPtr = (Text *) clientData;
  2838.     char *text;
  2839.     int count;
  2840.  
  2841.     if (TextPtr->selectFirst < 0) {
  2842.     return -1;
  2843.     }
  2844.     count = TextPtr->selectLast + 1 - TextPtr->selectFirst - offset;
  2845.     if (count > maxBytes) {
  2846.     count = maxBytes;
  2847.     }
  2848.     if (count <= 0) {
  2849.     return 0;
  2850.     }
  2851.     text = GetText(TextPtr, TextPtr->selectFirst + offset, count);
  2852.  
  2853.     if (text != NULL) {
  2854.     strcpy(buffer, text);
  2855.     free(text);
  2856.     } else
  2857.     count = 0;
  2858.  
  2859.     return count;
  2860. }
  2861.  
  2862. /*
  2863.  *----------------------------------------------------------------------
  2864.  *
  2865.  * TextLostSelection --
  2866.  *
  2867.  *    This procedure is called back by Tk when the selection is
  2868.  *    grabbed away from a Text widget.
  2869.  *
  2870.  * Results:
  2871.  *    None.
  2872.  *
  2873.  * Side effects:
  2874.  *    The existing selection is unhighlighted, and the window is
  2875.  *    marked as not containing a selection.
  2876.  *
  2877.  *----------------------------------------------------------------------
  2878.  */
  2879. static void
  2880. TextLostSelection(clientData)
  2881.     ClientData clientData;    /* Information about Text widget. */
  2882. {
  2883.     Text *TextPtr = (Text *) clientData;
  2884.  
  2885.     TextPtr->selectFirst = -1;
  2886.     TextPtr->selectLast = -1;
  2887.     EventuallyRedraw(TextPtr);
  2888. }
  2889.  
  2890. #endif
  2891.  
  2892. /*
  2893.  *----------------------------------------------------------------------
  2894.  *
  2895.  * GotoLine --
  2896.  *
  2897.  *    Move the top line of the viewport to the new location based
  2898.  *    upon the given line number.  Force outlier requests to the
  2899.  *    top or bottom of text.
  2900.  *
  2901.  * Results:
  2902.  *    Stanard Tcl result. If TCL_OK, interp->result contains the
  2903.  *    current line number.
  2904.  *
  2905.  * Side effects:
  2906.  *    At the next idle point, the text viewport will be move to the
  2907.  *    new line.
  2908.  *
  2909.  *----------------------------------------------------------------------
  2910.  */
  2911. static int
  2912. GotoLine(textPtr, interp, argc, argv)
  2913.     Htext *textPtr;
  2914.     Tcl_Interp *interp;
  2915.     int argc;
  2916.     char **argv;
  2917. {
  2918.     int line;
  2919.  
  2920.     if (argc > 3) {
  2921.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2922.         " gotoline ?line?\"", (char *)NULL);
  2923.     return TCL_ERROR;
  2924.     }
  2925.     line = textPtr->first;
  2926.     if (argc == 3) {
  2927.     if ((argv[2][0] == 'e') && (strcmp(argv[2], "end") == 0)) {
  2928.         line = textPtr->numLines - 1;
  2929.     } else {
  2930.         if (Tcl_GetInt(textPtr->interp, argv[2], &line) != TCL_OK) {
  2931.         return TCL_ERROR;
  2932.         }
  2933.         /* Keep the line request within the current range of lines */
  2934.         if (line < 1) {
  2935.         line = 1;
  2936.         } else if (line > textPtr->numLines) {
  2937.         line = textPtr->numLines;
  2938.         }
  2939.         line--;
  2940.     }
  2941.     textPtr->reqLineNum = line;
  2942.     textPtr->flags |= VIEW_MOVED;
  2943.  
  2944.     /*
  2945.      * Make only a request for a change in the viewport.  Defer
  2946.      * the actual scrolling until the text layout is adjusted at
  2947.      * the next idle point.
  2948.      */
  2949.     if (line != textPtr->first) {
  2950.         textPtr->flags |= REQUEST_GOTO;
  2951.         EventuallyRedraw(textPtr);
  2952.     }
  2953.     }
  2954.     sprintf(textPtr->interp->result, "%d", line + 1);
  2955.     return TCL_OK;
  2956. }
  2957.  
  2958. static int
  2959. ScrollX(textPtr, string)
  2960.     Htext *textPtr;
  2961.     char *string;
  2962. {
  2963.     int x;
  2964.  
  2965.     x = textPtr->vPort.x;
  2966.     if (string != NULL) {
  2967.     if (Tk_GetPixels(textPtr->interp,
  2968.         textPtr->tkwin, string, &x) != TCL_OK) {
  2969.         return TCL_ERROR;
  2970.     }
  2971.     x *= textPtr->scrollX;    /* Convert to pixels */
  2972.     if (x > textPtr->vPort.width) {
  2973.         x = textPtr->vPort.width - 1;
  2974.     } else if (x < 0) {
  2975.         x = 0;
  2976.     }
  2977.     if (x != textPtr->vPort.x) {
  2978.         textPtr->pendingX = x;
  2979.         textPtr->flags |= VIEW_MOVED;
  2980.         EventuallyRedraw(textPtr);
  2981.     }
  2982.     }
  2983.     sprintf(textPtr->interp->result, "%d", x / textPtr->scrollX);
  2984.     return TCL_OK;
  2985. }
  2986.  
  2987.  
  2988. static int
  2989. ScrollY(textPtr, string)
  2990.     Htext *textPtr;
  2991.     char *string;
  2992. {
  2993.     int y;
  2994.  
  2995.     y = textPtr->vPort.y;
  2996.     if (string != NULL) {
  2997.     if (Tk_GetPixels(textPtr->interp,
  2998.         textPtr->tkwin, string, &y) != TCL_OK) {
  2999.         return TCL_ERROR;
  3000.     }
  3001.     y *= textPtr->scrollY;    /* Convert to pixels */
  3002.     if (y > textPtr->vPort.height) {
  3003.         y = textPtr->vPort.height - 1;
  3004.     } else if (y < 0) {
  3005.         y = 0;
  3006.     }
  3007.     if (y != textPtr->vPort.y) {
  3008.         textPtr->pendingY = y;
  3009.         textPtr->flags |= VIEW_MOVED;
  3010.         EventuallyRedraw(textPtr);
  3011.     }
  3012.     }
  3013.     sprintf(textPtr->interp->result, "%d", y / textPtr->scrollY);
  3014.     return TCL_OK;
  3015. }
  3016.  
  3017. /*
  3018.  *----------------------------------------------------------------------
  3019.  *
  3020.  * GetChildWindows --
  3021.  *
  3022.  *    Returns a list of all the pathNames of child windows of the
  3023.  *    Htext widget.  If a pattern argument is given, only the names
  3024.  *    of windows matching it will be placed into the list.
  3025.  *
  3026.  * Results:
  3027.  *    Standard Tcl result.  If TCL_OK, interp->result will contain
  3028.  *    the list of the child window pathnames.  Otherwise it will
  3029.  *    contain an error message.
  3030.  *
  3031.  *----------------------------------------------------------------------
  3032.  */
  3033. static int
  3034. GetChildren(textPtr, interp, argc, argv)
  3035.     Htext *textPtr;        /* Hypertext widget record */
  3036.     Tcl_Interp *interp;        /* Interpreter associated with widget */
  3037.     int argc;
  3038.     char **argv;
  3039. {
  3040.     Child *childPtr;
  3041.     Tcl_HashEntry *entryPtr;
  3042.     Tcl_HashSearch searchId;
  3043.     char *name;
  3044.  
  3045.     if ((argc != 2) && (argc != 3)) {
  3046.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3047.         " children ?pattern?\"", (char *)NULL);
  3048.     return TCL_ERROR;
  3049.     }
  3050.     for (entryPtr = Tcl_FirstHashEntry(&(textPtr->subwindows), &searchId);
  3051.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&searchId)) {
  3052.     childPtr = (Child *)Tcl_GetHashValue(entryPtr);
  3053.     if (childPtr->tkwin == NULL) {
  3054.         fprintf(stderr, "window `%s' is null\n",
  3055.         Tk_PathName(Tcl_GetHashKey(&(textPtr->subwindows), entryPtr)));
  3056.         continue;
  3057.     }
  3058.     name = Tk_PathName(childPtr->tkwin);
  3059.     if ((argc == 2) || (Tcl_StringMatch(name, argv[2]))) {
  3060.         Tcl_AppendElement(interp, name);
  3061.     }
  3062.     }
  3063.     return TCL_OK;
  3064. }
  3065.  
  3066. /*
  3067.  *----------------------------------------------------------------------
  3068.  *
  3069.  * ConfigureParent --
  3070.  *
  3071.  *     This procedure is called to process an argv/argc list, plus
  3072.  *    the Tk option database, in order to configure (or reconfigure)
  3073.  *    a hypertext widget.
  3074.  *
  3075.  * Results:
  3076.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  3077.  *     returned, then interp->result contains an error message.
  3078.  *
  3079.  * Side effects:
  3080.  *    Configuration information, such as text string, colors, font,
  3081.  *     etc. get set for textPtr;  old resources get freed, if there were any.
  3082.  *     The hypertext is redisplayed.
  3083.  *
  3084.  *----------------------------------------------------------------------
  3085.  */
  3086. static int
  3087. ConfigureParent(textPtr, interp, argc, argv)
  3088.     Htext *textPtr;
  3089.     Tcl_Interp *interp;
  3090.     int argc;
  3091.     char **argv;
  3092. {
  3093.     if (argc == 2) {
  3094.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  3095.         (char *)textPtr, (char *)NULL, 0));
  3096.     } else if (argc == 3) {
  3097.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
  3098.         (char *)textPtr, argv[2], 0));
  3099.     } else {
  3100.     if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2,
  3101.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  3102.         return TCL_ERROR;
  3103.     }
  3104.     EventuallyRedraw(textPtr);
  3105.     }
  3106.     return TCL_OK;
  3107. }
  3108.  
  3109. /*
  3110.  * ----------------------------------------------------------------------
  3111.  *
  3112.  * ConfigureChild --
  3113.  *
  3114.  *     This procedure is called to process an argv/argc list, plus
  3115.  *    the Tk option database, in order to configure (or reconfigure)
  3116.  *    a hypertext child.
  3117.  *
  3118.  * Results:
  3119.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  3120.  *     returned, then interp->result contains an error message.
  3121.  *
  3122.  * Side effects:
  3123.  *    Configuration information get set for the child cavity; old
  3124.  *    resources get freed, if there were any. The child is marked
  3125.  *    for redisplay.
  3126.  *
  3127.  *----------------------------------------------------------------------
  3128.  */
  3129. static int
  3130. ConfigureChild(textPtr, interp, argc, argv)
  3131.     Htext *textPtr;
  3132.     Tcl_Interp *interp;
  3133.     int argc;
  3134.     char **argv;
  3135. {
  3136.     Child *childPtr;
  3137.  
  3138.     if (argc < 3) {
  3139.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3140.         " childconfigure childName ?args...?\"", (char *)NULL);
  3141.     return TCL_ERROR;
  3142.     }
  3143.     childPtr = FindChild(textPtr, argv[2]);
  3144.     if (childPtr == NULL) {
  3145.     Tcl_AppendResult(interp, "can't find window \"", argv[2], "\" in \"",
  3146.         Tk_PathName(textPtr->tkwin), "\"", (char *)NULL);
  3147.     return TCL_ERROR;
  3148.     }
  3149.     if (argc == 3) {
  3150.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
  3151.         (char *)childPtr, (char *)NULL, 0));
  3152.     } else if (argc == 4) {
  3153.     return (Tk_ConfigureInfo(interp, textPtr->tkwin, childConfigSpecs,
  3154.         (char *)childPtr, argv[3], 0));
  3155.     } else {
  3156.     if (Tk_ConfigureWidget(interp, textPtr->tkwin, childConfigSpecs,
  3157.         argc - 3, argv + 3, (char *)childPtr,
  3158.         TK_CONFIG_ARGV_ONLY) != TCL_OK) {
  3159.         return TCL_ERROR;
  3160.     }
  3161.     textPtr->flags |= REQUEST_LAYOUT;
  3162.     EventuallyRedraw(textPtr);
  3163.     }
  3164.     return TCL_OK;
  3165. }
  3166.  
  3167. /*
  3168.  *----------------------------------------------------------------------
  3169.  *
  3170.  * ScanText --
  3171.  *
  3172.  *    Implements the quick scan for hypertext widgets.
  3173.  *
  3174.  *----------------------------------------------------------------------
  3175.  */
  3176. static int
  3177. ScanText(textPtr, interp, argc, argv)
  3178.     Htext *textPtr;
  3179.     Tcl_Interp *interp;
  3180.     int argc;
  3181.     char **argv;
  3182. {
  3183.     int markX, markY;
  3184.     char c;
  3185.     int length;
  3186.  
  3187.     if (argc != 5) {
  3188.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3189.         " scan mark|dragto x y\"", (char *)NULL);
  3190.     return TCL_ERROR;
  3191.     }
  3192.     if ((Tcl_GetInt(interp, argv[3], &markX) != TCL_OK) ||
  3193.     (Tcl_GetInt(interp, argv[4], &markY) != TCL_OK)) {
  3194.     return TCL_ERROR;
  3195.     }
  3196.     c = argv[2][0];
  3197.     length = strlen(argv[2]);
  3198.     if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
  3199.     textPtr->scanMark.x = markX, textPtr->scanMark.y = markY;
  3200.     textPtr->scanPt.x = textPtr->vPort.x;
  3201.     textPtr->scanPt.y = textPtr->vPort.y;
  3202.  
  3203.     } else if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
  3204.     int x, y;
  3205.  
  3206.     x = textPtr->scanPt.x - (10 * (markX - textPtr->scanMark.x));
  3207.     y = textPtr->scanPt.y - (10 * (markY - textPtr->scanMark.y));
  3208.  
  3209.     if (x < 0) {
  3210.         x = textPtr->scanPt.x = 0;
  3211.         textPtr->scanMark.x = markX;
  3212.     } else if (x >= textPtr->vPort.width) {
  3213.         x = textPtr->scanPt.x = textPtr->vPort.width - textPtr->scrollX;
  3214.         textPtr->scanMark.x = markX;
  3215.     }
  3216.     if (y < 0) {
  3217.         y = textPtr->scanPt.y = 0;
  3218.         textPtr->scanMark.y = markY;
  3219.     } else if (y >= textPtr->vPort.height) {
  3220.         y = textPtr->scanPt.y = textPtr->vPort.height - textPtr->scrollY;
  3221.         textPtr->scanMark.y = markY;
  3222.     }
  3223.     if ((y != textPtr->pendingY) || (x != textPtr->pendingX)) {
  3224.         textPtr->pendingX = x, textPtr->pendingY = y;
  3225.         textPtr->flags |= VIEW_MOVED;
  3226.         EventuallyRedraw(textPtr);
  3227.     }
  3228.     } else {
  3229.     Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  3230.         "\":  must be mark or dragto", (char *)NULL);
  3231.     return TCL_ERROR;
  3232.     }
  3233.     return TCL_OK;
  3234. }
  3235.  
  3236. /*
  3237.  *----------------------------------------------------------------------
  3238.  *
  3239.  * SearchText --
  3240.  *
  3241.  *    Returns the linenumber of the next line matching the given
  3242.  *    pattern within the range of lines provided.  If the first
  3243.  *    line number is greater than the last, the search is done in
  3244.  *    reverse.
  3245.  *
  3246.  *----------------------------------------------------------------------
  3247.  */
  3248. static int
  3249. SearchText(textPtr, interp, argc, argv)
  3250.     Htext *textPtr;
  3251.     Tcl_Interp *interp;
  3252.     int argc;
  3253.     char **argv;
  3254. {
  3255.     int first, last;
  3256.     register int i;
  3257.     register char *string;
  3258.     int lineNum;
  3259.     enum Direction {
  3260.     REVERSE = -1, FORWARD = 1
  3261.     } direction;        /* Indicates the direction of the search */
  3262.  
  3263.     if ((argc < 3) || (argc > 5)) {
  3264.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3265.         " search pattern ?from? ?to?\"", (char *)NULL);
  3266.     return TCL_ERROR;
  3267.     }
  3268.     first = 0;
  3269.     last = textPtr->numLines - 1;
  3270.  
  3271.     if (argc > 3) {
  3272.     if (argv[3][0] == 'e' && strcmp(argv[3], "end") == 0) {
  3273.         first = textPtr->numLines - 1;
  3274.     } else {
  3275.         if (Tcl_GetInt(interp, argv[3], &first) != TCL_OK) {
  3276.         return TCL_ERROR;
  3277.         }
  3278.         first--;
  3279.         if (first < 0) {
  3280.         first = 0;
  3281.         } else if (first >= textPtr->numLines) {
  3282.         first = textPtr->numLines - 1;
  3283.         }
  3284.     }
  3285.     }
  3286.     if (argc > 4) {
  3287.     if ((argv[4][0] == 'e') && (strcmp(argv[4], "end") == 0)) {
  3288.         last = textPtr->numLines - 1;
  3289.     } else {
  3290.         if (Tcl_GetInt(interp, argv[4], &last) != TCL_OK) {
  3291.         return TCL_ERROR;
  3292.         }
  3293.         last--;
  3294.         if (last < 0) {
  3295.         last = 0;
  3296.         } else if (last >= textPtr->numLines) {
  3297.         last = textPtr->numLines - 1;
  3298.         }
  3299.     }
  3300.     }
  3301.     direction = (first > last) ? REVERSE : FORWARD;
  3302.     lineNum = -1;
  3303.     for (i = first; i != (last + direction); i += direction) {
  3304.     string = textPtr->linePtrPtr[i]->text;
  3305.     if ((string != NULL) && Tcl_StringMatch(string, argv[2])) {
  3306.         lineNum = i + 1;
  3307.         break;
  3308.     }
  3309.     }
  3310.     sprintf(textPtr->interp->result, "%d", lineNum);
  3311.     return TCL_OK;
  3312. }
  3313.  
  3314. /*
  3315.  * --------------------------------------------------------------
  3316.  *
  3317.  * TextWidgetCmd --
  3318.  *
  3319.  *     This procedure is invoked to process the Tcl command that
  3320.  *    corresponds to a widget managed by this module. See the user
  3321.  *     documentation for details on what it does.
  3322.  *
  3323.  * Results:
  3324.  *    A standard Tcl result.
  3325.  *
  3326.  * Side effects:
  3327.  *    See the user documentation.
  3328.  *
  3329.  * --------------------------------------------------------------
  3330.  */
  3331. static int
  3332. TextWidgetCmd(clientData, interp, argc, argv)
  3333.     ClientData clientData;    /* Information about hypertext widget. */
  3334.     Tcl_Interp *interp;        /* Current interpreter. */
  3335.     int argc;            /* Number of arguments. */
  3336.     char **argv;        /* Argument strings. */
  3337. {
  3338.     register Htext *textPtr = (Htext *)clientData;
  3339.     int result = TCL_OK;
  3340.     int length;
  3341.     char c;
  3342.  
  3343.     if (argc < 2) {
  3344.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3345.         " option ?arg arg ...?\"", (char *)NULL);
  3346.     return TCL_ERROR;
  3347.     }
  3348.     Tk_Preserve((ClientData)textPtr);
  3349.     c = argv[1][0];
  3350.     length = strlen(argv[1]);
  3351.  
  3352.     if ((c == 'a') && (strncmp(argv[1], "append", length) == 0)) {
  3353.     result = AppendChild(textPtr, interp, argc, argv);
  3354.     } else if ((c == 'c') && (length > 5) &&
  3355.     (strncmp(argv[1], "childconfigure", length) == 0)) {
  3356.     result = ConfigureChild(textPtr, interp, argc, argv);
  3357.     } else if ((c == 'c') && (length > 5) &&
  3358.     (strncmp(argv[1], "children", length) == 0)) {
  3359.     result = GetChildren(textPtr, interp, argc, argv);
  3360.     } else if ((c == 'c') && (length > 1) &&
  3361.     (strncmp(argv[1], "configure", length) == 0)) {
  3362.     result = ConfigureParent(textPtr, interp, argc, argv);
  3363.     } else if ((c == 'c') && (length > 1) &&
  3364.            (strncmp(argv[1], "cget", length) == 0)) {
  3365.         if (argc != 3) {
  3366.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  3367.                     argv[0], " cget option\"", (char *) NULL);
  3368.             goto error;
  3369.         }
  3370.         result = Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,
  3371.                 (char *) textPtr, argv[2], 0);
  3372.     } else if ((c == 'g') && (strncmp(argv[1], "gotoline", length) == 0)) {
  3373.     result = GotoLine(textPtr, interp, argc, argv);
  3374.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  3375.     if (argc > 3) {
  3376.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3377.         " xview ?position?\"", (char *)NULL);
  3378.         goto error;
  3379.     }
  3380.     result = ScrollX(textPtr, argv[2]);
  3381.     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
  3382.     if (argc > 3) {
  3383.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3384.         " yview ?position?\"", (char *)NULL);
  3385.         goto error;
  3386.     }
  3387.     result = ScrollY(textPtr, argv[2]);
  3388.     } else if ((c == 's') && (length > 1)
  3389.     && (strncmp(argv[1], "scan", length) == 0)) {
  3390.     result = ScanText(textPtr, interp, argc, argv);
  3391.     } else if ((c == 's') && (length > 2) &&
  3392.     strncmp(argv[1], "search", length) == 0) {
  3393.     result = SearchText(textPtr, interp, argc, argv);
  3394. #ifdef notdef
  3395.     } else if ((c == 's') && (length > 1) &&
  3396.     strncmp(argv[1], "select", length) == 0) {
  3397.     int index;
  3398.  
  3399.     if (argc < 3) {
  3400.         Tcl_AppendResult(interp, "too few args: should be \"",
  3401.         argv[0], " select option ?index?\"", (char *)NULL);
  3402.         goto error;
  3403.     }
  3404.     length = strlen(argv[2]);
  3405.     c = argv[2][0];
  3406.     if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
  3407.         if (argc != 3) {
  3408.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3409.             argv[0], " select clear\"", (char *)NULL);
  3410.         goto error;
  3411.         }
  3412.         if (textPtr->selectFirst != -1) {
  3413.         textPtr->selectFirst = textPtr->selectLast = -1;
  3414.         goto redisplay;
  3415.         }
  3416.         goto error;
  3417.     }
  3418.     if (argc >= 4) {
  3419.         if (GetTextIndex(interp, textPtr, argv[3], &index) != TCL_OK) {
  3420.         goto error;
  3421.         }
  3422.     }
  3423.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  3424.         if (argc != 4) {
  3425.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3426.             argv[0], " select adjust index\"",
  3427.             (char *)NULL);
  3428.         goto error;
  3429.         }
  3430.         TextSelectTo(textPtr, index);
  3431.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  3432.         if (argc != 4) {
  3433.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3434.             argv[0], " select from index\"",
  3435.             (char *)NULL);
  3436.         goto error;
  3437.         }
  3438.         textPtr->selectAnchor = index;
  3439.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  3440.         if (argc != 4) {
  3441.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3442.             argv[0], " select to index\"", (char *)NULL);
  3443.         goto error;
  3444.         }
  3445.         TextSelectTo(textPtr, index);
  3446.     } else {
  3447.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  3448.         "\": must be adjust, clear, from, or to", (char *)NULL);
  3449.         goto error;
  3450.     }
  3451. #endif
  3452.     } else {
  3453.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\": should be ",
  3454.         "append, configure, childconfigure, gotoline, scan, search, xview, or yview",
  3455.         (char *)NULL);
  3456.     }
  3457.   error:
  3458.     Tk_Release((ClientData)textPtr);
  3459.     return result;
  3460. }
  3461.  
  3462. /*
  3463.  * --------------------------------------------------------------
  3464.  *
  3465.  * HtextCmd --
  3466.  *
  3467.  *     This procedure is invoked to process the "htext" Tcl command.
  3468.  *    See the user documentation for details on what it does.
  3469.  *
  3470.  * Results:
  3471.  *    A standard Tcl result.
  3472.  *
  3473.  * Side effects:
  3474.  *    See the user documentation.
  3475.  *
  3476.  * --------------------------------------------------------------
  3477.  */
  3478.  
  3479. static int
  3480. HtextCmd(clientData, interp, argc, argv)
  3481.     ClientData clientData;    /* Main window associated with interpreter. */
  3482.     Tcl_Interp *interp;        /* Current interpreter. */
  3483.     int argc;            /* Number of arguments. */
  3484.     char **argv;        /* Argument strings. */
  3485. {
  3486.     register Htext *textPtr;
  3487.     Tk_Window mainWindow = (Tk_Window)clientData;
  3488.     Tk_Window tkwin;
  3489.  
  3490.     if (argc < 2) {
  3491.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  3492.         " pathName ?options?\"", (char *)NULL);
  3493.     return TCL_ERROR;
  3494.     }
  3495.     textPtr = (Htext *)calloc(1, sizeof(Htext));
  3496.     if (textPtr == NULL) {
  3497.     interp->result = "can't allocate text structure";
  3498.     return TCL_ERROR;
  3499.     }
  3500.     tkwin = Tk_CreateWindowFromPath(interp, mainWindow, argv[1], (char *)NULL);
  3501.     if (tkwin == NULL) {
  3502.     free(textPtr);
  3503.     return TCL_ERROR;
  3504.     }
  3505.     /* Initialize the new hypertext widget */
  3506.  
  3507.     Tk_SetClass(tkwin, "Blt_htext");
  3508.     textPtr->tkwin = tkwin;
  3509.     textPtr->display = Tk_Display(tkwin);
  3510.     textPtr->interp = interp;
  3511.     textPtr->numLines = textPtr->arraySize = 0;
  3512.     textPtr->lineSpacing = 1;
  3513.     textPtr->scrollX = textPtr->scrollY = 10;
  3514.     textPtr->relief = TK_RELIEF_FLAT;
  3515.     Tcl_InitHashTable(&(textPtr->subwindows), TCL_ONE_WORD_KEYS);
  3516.  
  3517.     /*
  3518.      * -----------------------------------------------------------------
  3519.      *
  3520.      * Create the widget command before configuring the widget. This
  3521.      * is because the "-file" and "-text" options may have embedded
  3522.      * commands that self-reference the widget through the
  3523.      * "$blt_htext(widget)" variable.
  3524.      *
  3525.      * ------------------------------------------------------------------
  3526.      */
  3527.  
  3528.     Tcl_CreateCommand(interp, argv[1], TextWidgetCmd, (ClientData)textPtr,
  3529.     (Tcl_CmdDeleteProc *)NULL);
  3530.     Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
  3531.     TextEventProc, (ClientData)textPtr);
  3532.  
  3533.     if (ConfigureHtext(interp, textPtr, argc - 2, argv + 2, 0) != TCL_OK) {
  3534.     Tk_DestroyWindow(textPtr->tkwin);
  3535.     return TCL_ERROR;
  3536.     }
  3537.     Tcl_SetResult(interp, Tk_PathName(textPtr->tkwin), TCL_STATIC);
  3538.  
  3539.     return TCL_OK;
  3540. }
  3541.  
  3542. int
  3543. Blt_HtextInit(interp)
  3544.     Tcl_Interp *interp;
  3545. {
  3546.     Tk_Window tkwin;
  3547.  
  3548.     if (Blt_FindCmd(interp, HTEXT_CMDNAME, (ClientData *)NULL) == TCL_OK) {
  3549.     Tcl_AppendResult(interp, "\"blt_htext\" command already exists",
  3550.         (char *)NULL);
  3551.     return TCL_ERROR;
  3552.     }
  3553.     tkwin = Tk_MainWindow(interp);
  3554.     if (tkwin == NULL) {
  3555.     Tcl_AppendResult(interp, "\"blt_htext\" requires Tk", (char *)NULL);
  3556.     return TCL_ERROR;
  3557.     }
  3558.     Tcl_SetVar2(interp, "blt_versions", HTEXT_CMDNAME, HTEXT_VERSION,
  3559.     TCL_GLOBAL_ONLY);
  3560.     Tcl_CreateCommand(interp, HTEXT_CMDNAME, HtextCmd, (ClientData)tkwin,
  3561.     (Tcl_CmdDeleteProc *)NULL);
  3562.     return TCL_OK;
  3563. }
  3564.